• 智能指针篇


    目录

    1 link

    2 link

    4 link

     5 link


    shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。

    shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针

    成员函数:

    use_count 返回引用计数的个数

    unique 返回是否是独占所有权( use_count 为 1)

    swap 交换两个 shared_ptr 对象(即交换所拥有的对象)

    reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少

    get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如

    shared_ptr<int> sp(new int(1));

    sp 与 sp.get()是等价的。

    share_ptr的简单例子:

    1. int main(){
    2. string *s1 = new string("s1");
    3. shared_ptr<string> ps1(s1);
    4. shared_ptr<string> ps2;
    5. ps2 = ps1;
    6. cout << ps1.use_count()<<endl; //2
    7. cout<<ps2.use_count()<<endl; //2
    8. cout << ps1.unique()<<endl; //0
    9. string *s3 = new string("s3");
    10. shared_ptr<string> ps3(s3);
    11. cout << (ps1.get()) << endl; //033AEB48
    12. cout << ps3.get() << endl; //033B2C50
    13. swap(ps1, ps3); //交换所拥有的对象
    14. cout << (ps1.get())<<endl; //033B2C50
    15. cout << ps3.get() << endl; //033AEB48
    16. cout << ps1.use_count()<<endl; //1
    17. cout << ps2.use_count() << endl; //2
    18. ps2 = ps1;
    19. cout << ps1.use_count()<<endl; //2
    20. cout << ps2.use_count() << endl; //2
    21. ps1.reset(); //放弃ps1的拥有权,引用计数的减少
    22. cout << ps1.use_count()<<endl; //0
    23. cout << ps2.use_count()<<endl; //1
    24. }

    引用计数不是垃圾回收,引用计数能够尽快收回不再被使用的对象,同时在回收的过程中也不会造成长时间的等待, 更能够清晰明确的表明资源的生命周期。

    std::shared_ptr

    std::shared_ptr 是一种智能指针,它能够记录多少个 shared_ptr 共同指向一个对象,从而消除显式的调用 delete,当引用计数变为零的时候就会将对象自动删除。

    但还不够,因为使用 std::shared_ptr 仍然需要使用 new 来调用,这使得代码出现了某种程度上的不对称。

    std::make_shared 就能够用来消除显式的使用 new,所以 std::make_shared 会分配创建传入参数中的对象, 并返回这个对象类型的 std::shared_ptr 指针。例如:

    1. #include <iostream>
    2. #include <memory>
    3. void foo(std::shared_ptr<int> i)
    4. {
    5. (*i)++;
    6. }
    7. int main()
    8. {
    9. // auto pointer = new int(10); // illegal, no direct assignment
    10. // Constructed a std::shared_ptr
    11. auto pointer = std::make_shared<int>(10);
    12. foo(pointer);
    13. std::cout << *pointer << std::endl; // 11
    14. // The shared_ptr will be destructed before leaving the scope
    15. return 0;
    16. }

    std::shared_ptr 可以通过 get() 方法来获取原始指针,通过 reset() 来减少一个引用计数, 并通过 use_count() 来查看一个对象的引用计数。例如:

    1. auto pointer = std::make_shared<int>(10);
    2. auto pointer2 = pointer; // 引用计数+1
    3. auto pointer3 = pointer; // 引用计数+1
    4. int *p = pointer.get(); // 这样不会增加引用计数
    5. std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
    6. std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
    7. std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
    8. pointer2.reset();
    9. std::cout << "reset pointer2:" << std::endl;
    10. std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
    11. std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2reset
    12. std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
    13. pointer3.reset();
    14. std::cout << "reset pointer3:" << std::endl;
    15. std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
    16. std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
    17. std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3reset

    std::unique_ptr

    std::unique_ptr 是一种独占的智能指针,它禁止其他智能指针与其共享同一个对象,从而保证代码的安全:

    std::unique_ptr pointer = std::make_unique(10); // make_unique 从 C++14 引入
    std::unique_ptr pointer2 = pointer; // 非法
    

    make_unique 并不复杂,C++11 没有提供 std::make_unique,可以自行实现:

    template
    std::unique_ptr make_unique( Args&& ...args ) {
      return std::unique_ptr( new T( std::forward(args)... ) );
    }
    

    至于为什么没有提供,C++ 标准委员会主席 Herb Sutter 在他的博客中提到原因是因为『被他们忘记了』。

    既然是独占,换句话说就是不可复制。但是,我们可以利用 std::move 将其转移给其他的 unique_ptr,例如:

    #include 
    #include 
    
    struct Foo {
        Foo() { std::cout << "Foo::Foo" << std::endl; }
        ~Foo() { std::cout << "Foo::~Foo" << std::endl; }
        void foo() { std::cout << "Foo::foo" << std::endl; }
    };
    
    void f(const Foo &) {
        std::cout << "f(const Foo&)" << std::endl;
    }
    
    int main() {
        std::unique_ptr p1(std::make_unique());
        // p1 不空, 输出
        if (p1) p1->foo();
        {
            std::unique_ptr p2(std::move(p1));
            // p2 不空, 输出
            f(*p2);
            // p2 不空, 输出
            if(p2) p2->foo();
            // p1 为空, 无输出
            if(p1) p1->foo();
            p1 = std::move(p2);
            // p2 为空, 无输出
            if(p2) p2->foo();
            std::cout << "p2 被销毁" << std::endl;
        }
        // p1 不空, 输出
        if (p1) p1->foo();
        // Foo 的实例会在离开作用域时被销毁
    }

    3 link 

    C++11中提供了三种不同类型的智能指针,分别是:

    共享智能指针std::shared_ptr独占智能指针std::unique_ptr弱智能指针std::weak_ptr

    共享智能指针使用引用计数,每个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr被析构的时候,内存被释放。

    初始化共享智能指针

    初始化共享智能指针有两种方法,第一种是通过new来直接初始化,第二种方式是通过make_shared方法与reset方法来初始化,这部分的代码演示如下:

    1. // make_shared初始化
    2. int a = 1;
    3. std::shared_ptr<int> a1 = std::make_shared<int>(a);
    4. std::cout << "a1: "<<*a1 << std::endl;
    5. // 直接初始化
    6. std::shared_ptr<int> a2 = std::shared_ptr<int>(new int(2));
    7. std::cout << "a2: " << *a2 << std::endl;

    一个原始指针最多只能初始化一个智能指针、而且可以从智能指针中通过get方法获取原始指针,这部分的代码演示如下:

    1. // 一个原始指针,只能初始化一个智能指针
    2. int* ptr = new int(12);
    3. std::shared_ptr<int> a3(ptr);
    4. // std::shared_ptra4(ptr); //直接挂了!
    5. // 从智能指针中获取原始指针
    6. int* pp = a3.get();
    7. std::cout << "pp: " << *pp << std::endl;

    共享智能指针的代码演示

    下面是共享智能指针一个典型的应用场景,就是在std::vector中使用共享智能指针来管理容器的对象或者数据。假设有个结构体,对象如下:

    typedef struct person {    std::string name;    int age;};

    然后初始化std::vector如下:

    1. std::vector<person> persons;
    2. person p3;
    3. p3.name = "opencv4";
    4. p3.age = 20;
    5. person p4;
    6. p4.name = "opencv5";
    7. p4.age = 21;
    8. persons.emplace_back(p3);
    9. persons.emplace_back(p4);

    然后有一天有个经常写C#或者Java的人,想更新std::vector结构体里面的属性值,于是它就遍历每个元素,然后开始修改,代码如下:​​​​​​​

    for (auto item : persons) { // 修改的是副本  item.age = 24;}

    测试运行结果如下:​​​​​​​

    for (auto item : persons) {  std::cout << "item.age: " << item.age << std::endl;}

    发现翻车了,原因是C++来说,通过for直接修改的不是引用,而是修改了副本!这个时候,只要改一个地方就立刻好了:

    1. for (auto &item : persons) { // 获取引用地址
    2.     item.age = 24;
    3. }
    4. for (auto item : persons) {
    5.     std::cout << "item.age: " << item.age << std::endl;
    6. }

    改用智能指针

    在初始化时候使用共享智能指针来完成元素组装,代码演示如下:

    1. std::vector<std::shared_ptr<person>> pps;
    2. person x;
    3. auto p5 = std::make_shared<person>(x);
    4. p5->name = "make_shared";
    5. auto p1 = std::shared_ptr<person>(new person());
    6. auto p2 = std::shared_ptr<person>(new person());
    7. p1->age = 12;
    8. p1->name = "qing";
    9. p2->age = 32;
    10. p2->name = "zhigang";
    11. pps.emplace_back(p1);
    12. pps.emplace_back(p2);
    13. pps.emplace_back(p5);
    14. for (auto item : pps) {
    15.     item->age = 24;
    16. }
    17. for (auto item : pps) {
    18.     std::cout << "item->name: " << item->name << " item->age:" << item->age << std::endl;
    19. }

    运行结果如下:

    最后,推荐使用make_shared来初始化共享智能指针

    unique_ptr因为其局限性(独享所有权),一般很少用于多线程操作。在多线程操作的时候,既可以共享资源,又可以自动释放资源,这就引入了shared_ptr。

    shared_ptr为了支持跨线程访问,其内部有一个引用计数(线程安全),用来记录当前使用该资源的shared_ptr个数,在结束使用的时候,引用计数为-1,当引用计数为0时,会自动释放其关联的资源。

    特点 相对于unique_ptr的独享所有权,shared_ptr可以共享所有权。其内部有一个引用计数,用来记录共享该资源的shared_ptr个数,当共享数为0的时候,会自动释放其关联的资源。

    对比unique_ptr,shared_ptr不支持数组,所以,如果用shared_ptr指向一个数组的话,需要自己手动实现deleter,如下所示:

    1

    std::shared_ptr<int> p(new int[8], [](int *ptr){delete []ptr;});

     5 link​​​​​​​

    make_shared函数是最安全分配和使用动态内存的方法,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。

    shared_ptr<int> p3 = make_shared<int>(42);//指向一个值为42的int的shared_ptr;

    智能指针是模板,当我们创建一个智能指针时候,必须提供额外信息------指针可以指向的类型,与vector一样,我们在<>中给出类型,之后就是所定义的这种智能指针的名字:

  • 相关阅读:
    MySQL 8.2 – 透明的读写分离(译)
    服务器是什么
    AntV G6 dom节点绑定事件问题
    免费域名证书最新申请方式大全
    车载T-BOX
    神仙打架!腾讯云阿里云谁更棋高一着?
    ICLR‘23论文得分排名! 多篇论文竟同时获1分和10分?
    边缘计算物联网网关在机械加工行业的应用及作用分享
    Java 枚举类
    Vue3中使用Proxy API取代defineProperty API的原因
  • 原文地址:https://blog.csdn.net/qq_35054151/article/details/128143448