C++中关于智能指针的总结

智能指针

一、shared_ptr

    shared_ptr采用的是共享所有权来管理所指向对象的生存期,它提供引用计数机制,记录有多少个shared_ptr指向同一块动态内存空间,只有最后一个shared_ptr被销毁时,才会自动释放该内存。

1、快速使用shared_ptr

#include <iostream>
#include <memory>
using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "MyClass constructed!" << endl;
    }
    ~MyClass() {
        cout << "MyClass destoryed!" << endl;
    }
};

int main() {

    shared_ptr<MyClass> sp1(new MyClass());
    shared_ptr<MyClass> sp2(sp1);

    cout << sp1.get() << " :" << sp1.use_count() << endl; // 2
    cout << sp2.get() << " :" << sp2.use_count() << endl; // 2

    return 0;
}

    上面代码中,sp1和sp2均为shared_ptr,sp1是通过new运算符返回的地址来构造出来的,它管理MyClass()对象;sp2通过拷贝构造函数,和sp1共同管理同一个对象的内存。因此,它们的使用的是同一个控制块,引用计数器的数值为2。

2、为何经常使用make_shared来创建一个shared_ptr,而不是直接使用shared_ptr的构造函数呢?

    使用make_shared相比于直接使用shared_ptr的构造函数主要有两个优势:① 内存分配效率高、② 异常安全。

①、内存分配效率高

    使用shared_ptr的构造函数创建一个shared_ptr对象需要分配两次内存:

shared_ptr<MyClass> sp1(new MyClass());

    上述代码中,首先为MyClass分配一次内存,然后为控制块(包含引用计数器等)分配一次内存,如下图所示:

    

    而使用make_shared的话,仅需要一次分配内存:

shared_ptr<MyClass> sp1 = make_shared<MyClass>();

    上述代码中,将一次性为MyClass和控制块分配内存,如下图所示:

②、异常安全

    下面这种情况下,使用make_shared不会造成内存泄漏,而使用shared_ptr的构造函数会造成内存泄漏:

func(shared_ptr<int>(new int(12)), shared_ptr<int>(new int (20)))

    由于C++是不保证参数求值顺序的,所以可能是下面这种顺序:

new int(12)
new int(20)
shared_ptr
shared_ptr

    上述顺序中,如果第二步new int(20)出现了异常,将导致第一步new int(12)分配的内存没有被shared_ptr管理,从而导致内存泄漏,而如果使用make_shared则可以避免这种情况。因为make_shared为管理对象和控制块分配内存是一起的,一旦管理对象的内存分配完毕,一定会有一个控制块来管理它。

    make_shared的使用方法如下:

func(make_shared<int>(12), make_shared<int>(20))

二、weak_ptr

1、快速使用weak_ptr

#include <iostream>
#include <memory>
using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "MyClass constructed!" << " - - " << this << endl;;
    }
    ~MyClass() {
        cout << "MyClass destoryed!" << endl;
    }
};

int main() {

    shared_ptr<MyClass> sp1 = make_shared<MyClass>();
    weak_ptr<MyClass> wp1(sp1);

    return 0;
}

2、weak_ptr是如何帮助shared_ptr解决循环引用问题的?

    下面代码中,发生了shared_ptr循环引用的情况,导致无法正常调用析构函数。

#include <iostream>
#include <memory>
using namespace std;
class YourClass;
class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructed!" << endl;
    }
    ~MyClass() {
        std::cout << "MyClass destoryed!" << endl;
    }
    void set_ptr(std::shared_ptr<YourClass>& ptr) {
        m_ptr_your = ptr;
    }
private:
    shared_ptr<YourClass> m_ptr_your;
};

class YourClass {
public:
    YourClass() {
        std::cout << "YourClass constructed!" << endl;
    }
    ~YourClass() {
        std::cout << "YourClass destoryed!" << endl;
    }
    void set_ptr(std::shared_ptr<MyClass>& ptr) {
        m_ptr_my = ptr;
    }
private:
    shared_ptr<MyClass> m_ptr_my;
};

int main()
{
    shared_ptr<MyClass> ptr_my(new MyClass());
    shared_ptr<YourClass> ptr_your(new YourClass());

    ptr_my->set_ptr(ptr_your);
    ptr_your->set_ptr(ptr_my);

    return 0;
}

    代码运行结果如下:

MyClass constructed!
YourClass constructed!

    从运行结果可以看出,该代码没有正确调用析构函数。

    循环引用的情况如下图所示:

    

    如果将MyClass中的m_ptr_your的指针类型从shared_ptr改成weak_ptr,那么它将打破循环引用的问题,从而正确调用析构函数。修改代码后运行结果如下:

MyClass constructed!
YourClass constructed!
YourClass destoryed!
MyClass destoryed!

    从运行结果可以看出,由于m_ptr_your使用的是weak_ptr指针,因此YourClass将会先调用析构函数,然后是MyClass调用析构函数。

三、unique_ptr

    unique采用的是独占所有权来管理所指向对象的生存期。也就是说,同一时刻,只能有一个unique_ptr指针指向这个对象,当这个unique_ptr被销毁的时候,它所指向的对象也会被销毁。

1、快速使用unique_ptr

#include <iostream>
#include <memory>
using namespace std;
class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructed!" << endl;
    }
    ~MyClass() {
        std::cout << "MyClass destoryed!" << endl;
    }
};

int main()
{
    unique_ptr<MyClass> up1(new MyClass());
    unique_ptr<MyClass> up2(up1);  // 不允许,unique_ptr禁用了拷贝构造函数
    unique_ptr<MyClass> up3 = up1; // 不允许,unique_ptr禁用了重载赋值运算符

    return 0;
}

猜你喜欢

转载自blog.csdn.net/hu853712064/article/details/120280039