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

    auto_ptr

    在 C++98 开始推出 auto_ptr 管理裸指针,让开发者不需要手动释放指针内存,当生命周期结束时自动释放内存。但是 auto_ptr 在转移所有权会出现野指针导致崩溃。

    root@ubuntu:~/c++# cat ptr.cpp 
    #include <iostream>
    #include <memory>
    void autoPtr()
    {
         std::auto_ptr<int> auto_ptr1(new int (1));
         std::auto_ptr<int> auto_ptr2 = auto_ptr1;  // 转移控制权
         *auto_ptr1 += 1;   // 程序崩溃
         // 由于auto_ptr1转移给auto_ptr2,auto_ptr1变为空指针,导致程序崩溃,可以通过get方法先判空
         if (auto_ptr1.get())
         { // 判空后执行
           // do somethings
         }
    }
    int main()
    {
       autoPtr();
       return 0;
    }
    root@ubuntu:~/c++#  g++ ptr.cpp  -o obj
    root@ubuntu:~/c++# ./obj
    Segmentation fault

    unique_ptr

    1、如何创建unique_ptr

    unique_ptr 不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。

    要想创建一个 unique_ptr,我们需要将一个 new 操作符返回的指针传递给unique_ptr的构造函数。

    示例:

    int main() {
        // 创建一个unique_ptr实例
        unique_ptr<int> pInt(new int(5));
        cout << *pInt;
    }

    2、无法进行复制构造和赋值操作

    unique_ptr没有 copy 构造函数,不支持普通的拷贝和赋值操作。

    示例:

    int main() {
        // 创建一个unique_ptr实例
        unique_ptr<int> pInt(new int(5));
        unique_ptr<int> pInt2(pInt);    // 报错
        unique_ptr<int> pInt3 = pInt;   // 报错
    }

    3、可以进行移动构造和移动赋值操作

    unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。

    如果需要转移所有权,可以使用std::move()函数。

    示例:

    int main() {
        unique_ptr<int> pInt(new int(5));
        unique_ptr<int> pInt2 = std::move(pInt);    // 转移所有权
        //cout << *pInt << endl; // 出错,pInt为空
        cout << *pInt2 << endl;
        unique_ptr<int> pInt3(std::move(pInt2));
    }

    4、可以返回unique_ptr

    unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。

    示例:

    unique_ptr<int> clone(int p)
    {
        unique_ptr<int> pInt(new int(p));
        return pInt;    // 返回unique_ptr
    }

    int main() {
        int p = 5;
        unique_ptr<int> ret = clone(p);
        cout << *ret << endl;
    }

    C++11 新出现unique_ptr智能指针管理内存,从命名上可以知道,该智能指针智能被唯一一个对
    象持有
    ,与 auto_ptr 一样拥有唯一拥有控制权的特性,不同的是 unique_ptr 是没有复制构造函数的,只能通过显示std::move()方法转移控制权。

    include <iostream>
    #include <memory>
    void uniq_ptr()
    {
         std::unique_ptr<int> unique_ptr1(new int (1));
         std::unique_ptr<int> unique_ptr2= unique_ptr1;  // 转移控制权
         *unique_ptr1+= 1;   // 程序崩溃
    }
    int main()
    {
       uniq_ptr();
       return 0;
    }
    int main() {
        // 创建一个unique_ptr实例
        unique_ptr<int> pInt(new int(5));
        unique_ptr<int> pInt2(pInt);    // 报错
        unique_ptr<int> pInt3 = pInt;   // 报错
    }
    root@ubuntu:~/c++#  g++ -std=c++11 -pthread ptr.cpp  -o obj
    ptr.cpp: In function ‘void uniq_ptr()’:
    ptr.cpp:6:40: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
          std::unique_ptr<int> unique_ptr2= unique_ptr1;  // 转移控制权
                                            ^
    In file included from /usr/include/c++/5/memory:81:0,
                     from ptr.cpp:2:
    /usr/include/c++/5/bits/unique_ptr.h:359:7: note: declared here
           unique_ptr(const unique_ptr&) = delete;
           ^
    #include <iostream>
    #include <memory>
    void uniq_ptr()
    {
         std::unique_ptr<int> unique_ptr1(new int (1));
         std::unique_ptr<int> unique_ptr2= std::move(unique_ptr1);  // 转移控制权
         *unique_ptr1+= 1;   // 程序崩溃
    }
    int main()
    {
       uniq_ptr();
       return 0;
    }
    ~
    root@ubuntu:~/c++#  g++ -std=c++11 -pthread ptr.cpp  -o obj
    root@ubuntu:~/c++# ./obj 
    Segmentation fault
    root@ubuntu:~/c++#
    #include <memory>
    #include <iostream>
    
    class A {
    public:
        A() {}
    
        ~A() {}
    };
    
    class B {
    public:
        B() : a(new A()) {
    
        }
    
        void output() {
            std::cout << "这是B对象" << std::endl;
        };
        void test() {
            std::cout << "this B  test " << std::endl;
        };
    
        ~B() {
            // 不释放a
            std::cout << "destruction B " << std::endl;
        }
    
    private:
        A *a;
    };
    
    int main(int argc, char *argv[]) {
        // B析构函数中A对象不会释放,导致内存泄露
        B *b = new B();
        b->test();
    
        // 使用unique_ptr会自动释放内存
        auto unique_ptr1 = std::unique_ptr<B>(new B());
        unique_ptr1->output();
    
        return 0;
    }
    root@ubuntu:~/c++#  g++ -std=c++11 obj.cpp  -o obj
    root@ubuntu:~/c++# ./obj
    this B  test 
    这是B对象
    destruction B

    只调用一次析构

    shared_ptr

    相对比unique_ptrshared_ptr支持复制,即多个对象共享同一块内存。采用引用计数的方式式实现内存的自动管理,当复制一次值 + 1,释放一次则值 - 1,直到值为 0 内存会被回收。

    #include <iostream>
    #include <memory>
    
    using namespace std;
    
    int main() {
    
        // 推荐方式
        shared_ptr<int> sharedPtr1(new int(5)); // 匿名函数
        shared_ptr<int> sharedPtr2 = make_shared<int>(5); // 工厂模式
    
        // 引用计数
        std::shared_ptr<int> sharedPtr3 = sharedPtr1;
        cout << sharedPtr1.use_count() << std::endl;  // 输出为2
        sharedPtr3.reset();                           // 释放
        cout << sharedPtr1.use_count() << endl;       // 输出为1
    
        return 0;
    }

    引用计数的方式引起循环引用导致无法被正常的释放,如下示例。

    #include <iostream>
    #include <memory>
    
    using namespace std;
    
    class Child;
    
    class Parent {
    public:
        shared_ptr<Child> child;
    };
    
    class Child {
    public:
        shared_ptr<Parent> parent;
    };
    
    int main() {
    
        shared_ptr<Parent> parentPtr = make_shared<Parent>();
        shared_ptr<Child> childPtr = make_shared<Child>();
        // 循环引用 导致内存永远不会被释放内存泄露
        parentPtr->child = childPtr;
        childPtr->parent = parentPtr;
    
        return 0;
    }

    ,shared_ptr 也支持移动

    #include <iostream>
    #include <memory>
    void share_ptr()
    {
         std::shared_ptr<int> shared_ptr1(new int (1));
         std::shared_ptr<int> shared_ptr2= std::move(shared_ptr1);  // 转移控制权
         *shared_ptr1+= 1;   // 程序崩溃
    }
    int main()
    {
       share_ptr();
       return 0;
    }
    root@ubuntu:~/c++#  g++ -std=c++11 share.cpp  -o obj
    root@ubuntu:~/c++# ./obj
    Segmentation fault
    root@ubuntu:~/c++# 
    #include <iostream>
    #include <memory>
    void share_ptr()
    {
         std::shared_ptr<int> shared_ptr1(new int (1));
         std::shared_ptr<int> shared_ptr2= std::move(shared_ptr1);  // 转移控制权
         *shared_ptr2+= 1;   // 程序正常
    }
    int main()
    {
       share_ptr();
       return 0;
    }
    root@ubuntu:~/c++#  g++ -std=c++11 share.cpp  -o obj
    root@ubuntu:~/c++# ./obj
    root@ubuntu:~/c++# 

    weak_ptr

    在上一 part 中介绍得share_ptr由于采用引用计数的方式,在循环引用时候时候 use_count 的值 + 1,导致释放后计数值依然为 1。而weak_ptr则用于解决此问题,share_ptr赋值给weak_ptr,不会引起引用计数值 + 1,只是在弱引用计数值 + 1,因此不会影响到share_ptr的生命周期。

    #include <memory>
    #include <iostream>
    
    class A {
    public:
        A() {}
    
        ~A() {}
    };
    
    class B {
    public:
        B() : a(new A()) {
    
        }
    
        void output() {
            std::cout << "这是B对象" << std::endl;
        };
        void test() {
            std::cout << "this B  test " << std::endl;
        };
    
        ~B() {
            // 不释放a
            std::cout << "destruction B " << std::endl;
        }
    
    private:
        A *a;
    };
    
    int main(int argc, char *argv[]) {
        B *b = new B();
        b->test();
    
        auto weak_ptr1 = std::weak_ptr<B>(new B());
        weak_ptr1->output();
    
        return 0;
    }
    obj.cpp: In function ‘int main(int, char**)’:
    obj.cpp:37:46: error: no matching function for call to ‘std::weak_ptr<B>::weak_ptr(B*)’
         auto weak_ptr1 = std::weak_ptr<B>(new B());
                                                  ^
    In file included from /usr/include/c++/5/memory:82:0,
                     from obj.cpp:1:
    /usr/include/c++/5/bits/shared_ptr.h:492:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(std::weak_ptr<_Tp1>&&)
      weak_ptr(weak_ptr<_Tp1>&& __r) noexcept
      ^
    /usr/include/c++/5/bits/shared_ptr.h:492:2: note:   template argument deduction/substitution failed:
    obj.cpp:37:46: note:   mismatched types ‘std::weak_ptr<_Tp>’ and ‘B*’
         auto weak_ptr1 = std::weak_ptr<B>(new B());
                                                  ^
    In file included from /usr/include/c++/5/memory:82:0,
                     from obj.cpp:1:
    /usr/include/c++/5/bits/shared_ptr.h:489:7: note: candidate: std::weak_ptr<_Tp>::weak_ptr(std::weak_ptr<_Tp>&&) [with _Tp = B]
           weak_ptr(weak_ptr&&) noexcept = default;
           ^
    /usr/include/c++/5/bits/shared_ptr.h:489:7: note:   no known conversion for argument 1 from ‘B*’ to ‘std::weak_ptr<B>&&/usr/include/c++/5/bits/shared_ptr.h:486:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(const std::weak_ptr<_Tp1>&)
      weak_ptr(const weak_ptr<_Tp1>& __r) noexcept
      ^
    /usr/include/c++/5/bits/shared_ptr.h:486:2: note:   template argument deduction/substitution failed:
    obj.cpp:37:46: note:   mismatched types ‘const std::weak_ptr<_Tp>’ and ‘B*’
         auto weak_ptr1 = std::weak_ptr<B>(new B());
                                                  ^
    In file included from /usr/include/c++/5/memory:82:0,
                     from obj.cpp:1:
    /usr/include/c++/5/bits/shared_ptr.h:483:7: note: candidate: std::weak_ptr<_Tp>::weak_ptr(const std::weak_ptr<_Tp>&) [with _Tp = B]
           weak_ptr(const weak_ptr&) noexcept = default;
           ^
    /usr/include/c++/5/bits/shared_ptr.h:483:7: note:   no known conversion for argument 1 from ‘B*’ to ‘const std::weak_ptr<B>&/usr/include/c++/5/bits/shared_ptr.h:480:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(const std::shared_ptr<_Tp1>&)
      weak_ptr(const shared_ptr<_Tp1>& __r) noexcept
      ^
    /usr/include/c++/5/bits/shared_ptr.h:480:2: note:   template argument deduction/substitution failed:
    obj.cpp:37:46: note:   mismatched types ‘const std::shared_ptr<_Tp1>’ and ‘B*’
         auto weak_ptr1 = std::weak_ptr<B>(new B());
                                                  ^
    In file included from /usr/include/c++/5/memory:82:0,
                     from obj.cpp:1:
    /usr/include/c++/5/bits/shared_ptr.h:477:17: note: candidate: constexpr std::weak_ptr<_Tp>::weak_ptr() [with _Tp = B]
           constexpr weak_ptr() noexcept = default;
                     ^
    /usr/include/c++/5/bits/shared_ptr.h:477:17: note:   candidate expects 0 arguments, 1 provided

    share_ptr给weak_ptr赋值

    #include <memory>
    #include <iostream>
    
    class A {
    public:
        A() {}
    
        ~A() {}
    };
    
    class B {
    public:
        B() : a(new A()) {
    
        }
    
        void output() {
            std::cout << "这是B对象" << std::endl;
        };
        void test() {
            std::cout << "this B  test " << std::endl;
        };
    
        ~B() {
            // 不释放a
            std::cout << "destruction B " << std::endl;
        }
    
    private:
        A *a;
    };
    
    int main(int argc, char *argv[]) {
        B *b = new B();
        b->test();
    
        auto weak_ptr1 =  std::make_shared<B>();
        weak_ptr1->output();
    
        return 0;
    }
    root@ubuntu:~/c++#  g++ -std=c++11 obj.cpp  -o obj
    root@ubuntu:~/c++# ./obj 
    this B  test 
    这是B对象
    destruction B
    int main(int argc, char *argv[]) {
        std::shared_ptr<B>  share_ptr1 = std::make_shared<B>();
        std::weak_ptr<B> weak_ptr1 = share_ptr1;
        weak_ptr1->output();
    
        return 0;
    }
    ~
    root@ubuntu:~/c++#  g++ -std=c++11 obj.cpp  -o obj
    obj.cpp: In function ‘int main(int, char**)’:
    obj.cpp:36:14: error: base operand of ‘->’ has non-pointer type ‘std::weak_ptr<B>’
         weak_ptr1->output();
                  ^
    root@ubuntu:~/c++# 
    int main(int argc, char *argv[]) {
        std::shared_ptr<B>  share_ptr1 = std::make_shared<B>();
        std::weak_ptr<B> weak_ptr1 = share_ptr1;
        weak_ptr1.lock()->output();
    
        return 0;
    }
    root@ubuntu:~/c++# ./obj
    这是B对象
    destruction B 
    root@ubuntu:~/c++# 
    int weak(std::shared_ptr<B> & sh) {
        std::weak_ptr<B> weak_ptr1 = sh;
        weak_ptr1.lock()->output();
        return 0;
    }
    int main(int argc, char *argv[]) {
        std::shared_ptr<B>  share_ptr1 = std::make_shared<B>();
        weak(share_ptr1);
        return 0;
    }
    root@ubuntu:~/c++# ./obj
    这是B对象
    destruction B 
    root@ubuntu:~/c++# 

     weak析构时,不调用对象的析构函数

    #include <iostream>
    #include <memory>
    
    using namespace std;
    
    class Child;
    
    class Parent {
    public:
        shared_ptr<Child> child;
    };
    
    class Child {
    public:
        weak_ptr<Parent> parent;  // 改为weak_ptr不会导致强引用计数值+1
    };
    
    int main() {
    
        shared_ptr<Parent> parentPtr = make_shared<Parent>();
        shared_ptr<Child> childPtr = make_shared<Child>();
        // 循环引用 导致内存永远不会被释放内存泄露
        parentPtr->child = childPtr;
        childPtr->parent = parentPtr;
    
        return 0;
    }

    接下来,我们来看看weak_ptr的简单用法。

     如何创建weak_ptr实例

    当我们创建一个weak_ptr时,需要用一个shared_ptr实例来初始化weak_ptr,由于是弱共享,weak_ptr的创建并不会影响shared_ptr的引用计数值。

    示例:

    int main() {
        shared_ptr<int> sp(new int(5));
        cout << "创建前sp的引用计数:" << sp.use_count() << endl;    // use_count = 1
    
        weak_ptr<int> wp(sp);
        cout << "创建后sp的引用计数:" << sp.use_count() << endl;    // use_count = 1
    }
     

    如何判断weak_ptr指向对象是否存在
    既然weak_ptr并不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。这时,我们就不能使用weak_ptr直接访问对象。那么我们如何判断weak_ptr指向对象是否存在呢?C++中提供了lock函数来实现该功能。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr,否则返回一个空shared_ptr。
    ————————————————
     

    class A
    {
    public:
        A() : a(3) { cout << "A Constructor..." << endl; }
        ~A() { cout << "A Destructor..." << endl; }
    
        int a;
    };
    
    int main() {
        shared_ptr<A> sp(new A());
        weak_ptr<A> wp(sp);
        //sp.reset();
    
        if (shared_ptr<A> pa = wp.lock())
        {
            cout << pa->a << endl;
        }
        else
        {
            cout << "wp指向对象为空" << endl;
        }
    }
     

    试试把sp.reset()这行的注释去掉看看结果有什么不同。

    除此之外,weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁。

    示例:

    class A
    {
    public:
        A() : a(3) { cout << "A Constructor..." << endl; }
        ~A() { cout << "A Destructor..." << endl; }
    
        int a;
    };
    
    int main() {
        shared_ptr<A> sp(new A());
        weak_ptr<A> wp(sp);
        sp.reset(); // 此时sp被销毁
        cout << wp.expired() << endl;  // true表示已被销毁,否则为false
    }
     
    A Constructor...
    A Destructor...
    1

    如何使用weak_ptr
    weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。

    最后,我们来看看如何使用weak_ptr来改造最前面的代码,打破循环引用问题。
     

    class ClassB;
    
    class ClassA
    {
    public:
        ClassA() { cout << "ClassA Constructor..." << endl; }
        ~ClassA() { cout << "ClassA Destructor..." << endl; }
        weak_ptr<ClassB> pb;  // 在A中引用B
    };
    
    class ClassB
    {
    public:
        ClassB() { cout << "ClassB Constructor..." << endl; }
        ~ClassB() { cout << "ClassB Destructor..." << endl; }
        weak_ptr<ClassA> pa;  // 在B中引用A
    };
    
    int main() {
        shared_ptr<ClassA> spa = make_shared<ClassA>();
        shared_ptr<ClassB> spb = make_shared<ClassB>();
        spa->pb = spb;
        spb->pa = spa;
        // 函数结束,思考一下:spa和spb会释放资源么?
    }
     
    ClassA Constructor...
    ClassB Constructor...
    ClassA Destructor...
    ClassB Destructor...
    Program ended with exit code: 0

    参考文章
    C++ 智能指针的正确使用方式
    C++ 知识点 | C11 智能指针

  • 相关阅读:
    Python几种主流框架
    Python测试框架
    Python测试工具开发
    robotFramework——通用的自动化测试框架
    依赖数据库的单元测试——DBUnit
    SpringTest——Spring在Junit上进一步封装,集成的测试模块
    linux---网络配置
    linux-----初学命令和理解
    Liunx----vi编辑器
    虚拟机---网络设置
  • 原文地址:https://www.cnblogs.com/dream397/p/14597297.html
Copyright © 2011-2022 走看看