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。





  • 相关阅读:
    108. Convert Sorted Array to Binary Search Tree
    107. Binary Tree Level Order Traversal II
    106. Construct Binary Tree from Inorder and Postorder Traversal
    105. Construct Binary Tree from Preorder and Inorder Traversal
    104. Maximum Depth of Binary Tree
    103. Binary Tree Zigzag Level Order Traversal
    102. Binary Tree Level Order Traversal
    系统和进程相关信息
    文件I/0缓冲
    系统编程概念(文件系统mount等函数的使用)
  • 原文地址:https://www.cnblogs.com/chenny7/p/8946704.html
Copyright © 2011-2022 走看看