zoukankan      html  css  js  c++  java
  • C++ 智能指针

    C++  智能指针

    unique_ptr

    大概长这个样子

    //大概长这个样子(化简版)
    template<class T>
    class unique_ptr{
      T* ptr;
    };

    unique_ptr是独占性智能指针,

    • 某个时刻只能有一个uniqueptr指向一个给定对象;
    • 当uniqueptr被销毁时,它所指向的对象也被销毁(自动对所指向对象调用delete);
    • uniqueptr不支持普通的拷贝和赋值操作;
    • 如果真的需要转移所有权(独占权),需要用std::move(std::unique_ptr对象)语法,转移所有权后如果仍使用 原有指针调用 会导致崩溃。

     例如:

    class Movie {
        public:
            Movie(string name) {
                this->name = name; 
                cout << "let's watch moive " << name << endl;
            }   
            ~Movie() {cout << "movie " << name << " end" << endl;}
            void getName() {cout<< "name=" << name << endl;}
    
        private:
            string name;
    };
    
    void test_unique_ptr() {
        unique_ptr<Movie> p1(new Movie("ne za"));
        unique_ptr<Movie> p2(new Movie("the lord of the ring"));
            
        //unique_ptr<Movie> p3 = p1;  // Error! 不支持赋值操作
        unique_ptr<Movie> p3 = move(p1); 
    
        //p1->getName();    // Error! 转移给p3以后,p1不再指向原对象
        p3->getName();
    }

     也可以用release方法来转移对象所有权,例如:

    void test_unique_ptr2() {
        unique_ptr<Movie> p1(new Movie("ne za"));
        unique_ptr<Movie> p2(new Movie("the lord of the ring"));
        
        p2.reset(p1.release());
        if (p1 == nullptr) {
            cout << "p1 is nullptr" << endl;
        }   
        p2.reset();
        if (p2 == nullptr) {
            cout << "p2 is nullptr" << endl;
        }   
    }

    p1.release() 方法转移其对象控制权(但并不会释放对象),并将p1赋空;

    p1.reset(p2) 方法释放p1指向的对象,并重新指向p2所指向的对象;

    p1.reset() 方法释放p1指向的对象,并赋空; 

    shared_ptr

    shared_ptr 是在使用引用计数的机制上提供了可以共享所有权的智能指针。

    大概长这个样子

    //大概长这个样子(化简版)
    struct SharedPtrControlBlock{
      int shared_count;
    };
    
    template<class T>
    class shared_ptr{
      T* ptr;
      SharedPtrControlBlock* count;
    };

    每次复制,多一个共享同处资源的shared_ptr时,计数+1;每次释放shared_ptr时,计数-1。

    当shared计数为0时,则证明所有指向同一处资源的shared_ptr们全都释放了,则随即释放该资源(哦,还会释放new出来的SharedPtrControlBlock)。

    初始化方式如下:

    shared_ptr<T> ptr(new T(), deleter);  // deleter 是自定义释放器,可以不传
    auto ptr = std::make_shared<T>()

    一个例子:

    void test_shared_ptr() {
        shared_ptr<Movie> p1(new Movie("ne za"));   // count = 1
        do {shared_ptr<Movie> p2 = p1;} while(0);   // count先加后减
        shared_ptr<Movie> p3 = p1;  // count = 2
        p1.reset();                 // count = 1
        //p3 = nullptr;             // count = 0
        cout << "test_shared_ptr " << endl;
    }

    如果把上面p3=nullptr这行的注释去掉,则会在这里导致计数器变0,从而引发对象的释放。

    shared_ptr类提供的成员方法:

    • get(),返回所指向的对象(指针)
    • reset(),放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少
    • use_count(),返回引用计数的个数
    • unique(),返回是否是独占所有权( use_count 为 1)
    • swap(),交换两个 shared_ptr 对象

      

    循环引用的情况:

    class Movie {
        public:
            Movie(string name) {
                this->name = name;
                cout << "let's watch moive " << name << endl;
            }
            ~Movie() {cout << "movie " << name << " end" << endl;}
            void getName() {cout<< "name=" << name << endl;}
            void setRelative(shared_ptr<Movie>& other) {
                relative_movie = other;
            }
    
        private:
            string name;
            shared_ptr<Movie> relative_movie;
    };
    
    
    void test_shared_ptr2() {
        shared_ptr<Movie> p1(new Movie("ne za"));
        shared_ptr<Movie> p2(new Movie("the lord of the ring")); 
        p1->setRelative(p2);
        p2->setRelative(p1);
        cout<<p1.use_count()<<endl; // 2
        cout<<p2.use_count()<<endl; // 2
    }
    上面的 test_shared_ptr2 函数执行完毕后,两个Movie对象析构函数都没有打印,也就是没有被释放。
    模拟下代码流程:
    1. 开始,两个Movie对象的计数都为2;
    2. p2指针退出栈,p2指向的Movie("the lord of the ring")计数器减为1;
    3. p1指针退出栈,p1指向的Movie("ne ze")计数器减为1;
    4. 函数退出,两个对象的计数都为1,都不会释放;

    为了解决这个问题,引入了weak_ptr指针。

    weak_ptr

    weak_ptr是一种不控制所指向对象生命周期的智能指针,它指向一个shared_ptr管理的对象。

    大概长这个样子:

    struct SharedPtrControlBlock{
      int shared_count;
      int weak_count;
    };
    
    template<class T>
    class weak_ptr{
      T* ptr;
      SharedPtrControlBlock* count;
    };

    特点

    • weak_ptr可以由一个shared_ptr,或者另一个weak_ptr构造;
    • weak_ptr的构造和析构不会引起shared_count的增加或减少,只会引起weak_count的增加或减少;
    • 当shared计数为0,会释放被管理资源,也就是说weak_ptr不控制资源的生命周期;
    • 计数区域的释放却取决于shared计数和weak计数,当两者均为0时,才会释放计数区域。

    例子

    class Movie {
        public:
            Movie(string name) {
                this->name = name;
                cout << "let's watch moive " << name << endl;
            }
            ~Movie() {cout << "movie " << name << " end" << endl;}
            void getName() {cout<< "name=" << name << endl;}
            void setRelative(shared_ptr<Movie>& other) {
                relative_movie = other;
            }
    
        private:
            string name;
            weak_ptr<Movie> relative_movie;
    };
    
    
    void test_weak_ptr() {
        shared_ptr<Movie> p1(new Movie("ne za"));
        shared_ptr<Movie> p2(new Movie("the lord of the ring"));
        p1->setRelative(p2);
        p2->setRelative(p1);
        cout<<p1.use_count()<<endl; // 1
        cout<<p2.use_count()<<endl; // 1
    }

    这个例子,两个对象都能够被释放;

     同样也模拟下代码流程:

    1. 开始,两个Movie对象的shared计数都为1,weak计数也为1;
    2. p2指针退出栈,p2指向的Movie("the lord of the ring")的shared计数器减为0,weak计数仍为1,资源释放,同时由于p2->relation_movie指向的Movie("ne ze")也被释放引起该资源的weak计数减为0;
    3. p1指针退出栈,p1指向的Movie("ne za")的shared计数器减为0,weak计数仍为1,资源释放,同时由于p1->relation_movie指向的Movie("the lord of the ring")也被释放引起该资源的weak计数减为0;
    4. 函数退出,两个对象的weak计数都为0,释放计数区域;

    另外,weak指针没有重载 * 和 -> ,所以并不能直接使用资源,但可以对weak指针调用lock方法,会返回一个shared_ptr指针。

    void test_weak_ptr2() {
        shared_ptr<Movie> p1(new Movie("ne za"));
        weak_ptr<Movie> p2 = p1; 
        //p2->getName();  // Erorr!
        shared_ptr<Movie> p3 = p2.lock();
        p3->getName();  
    }

    也就是说,弱引用特性,不拥有对象,只有延迟到尝试调用lock()时才会有可能临时拥有对象。

    使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。





  • 相关阅读:
    docker-compose写法收集
    【CodeForces】704 C. Black Widow 动态规划+模拟
    【BZOJ】2693: jzptab 莫比乌斯反演
    【BZOJ】2154: Crash的数字表格 莫比乌斯反演
    【CodeForces】915 G. Coprime Arrays 莫比乌斯反演
    【CodeForces】915 F. Imbalance Value of a Tree 并查集
    【CodeForces】915 E. Physical Education Lessons 线段树
    【CodeForces】915 D. Almost Acyclic Graph 拓扑排序找环
    【Atcoder】AGC 020 B
    【Atcoder】AGC 020 D
  • 原文地址:https://www.cnblogs.com/chenny7/p/8946704.html
Copyright © 2011-2022 走看看