C++11智能指针

智能指针
作用:管理一个指针,在函数结束时自动释放内存空间,不需要手动释放。(普通指针申请内存空间在函数结束时需手动释放内存,容易忘记,造成内存泄漏。智能指针很大程度上可以避免这个问题,因为智能指针是一个类,当超出类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。)

1.auto_ptr(c++98de 方案,c++11已经抛弃)
采用所有权模式。
auto_ptr p1(new string(“I reigned lonely as a cloud.”));
auto_ptr p2;
p2=p1;//auto_ptr不会报错
此时不会报错,p2剥夺了p1的所有权,但是当程序运行时访问p1将会报错。
缺点:存在潜在的内存崩溃问题。

2.unique_ptr(替换auto_ptr)
实现独占式拥有,保证同一时间内只有一个智能指针可以指向该对象。
auto_ptr p3(new string(“auto”));
auto_ptr p4;
p4=p3;//此时会报错
编译器认为p4=p3非法,避免了p3不再指向有效数据的问题,因此unique_ptr比auto_ptr更安全。
unique_ptr还有更聪明的地方:当程序试图将一个unique_ptr赋值给另一个时,如果源unique_ptr是一个临时右值,编译器允许这么做;如果源unique_ptr将存在一段时间,编译器将禁止这么做。比如:
auto_ptr pu1(new string(“hello world”));
auto_ptr pu2;
pu2=pu1;//#1不允许
unique_ptr pu3;
pu3=unique_ptr(new string(“You”));
其中#1留下悬挂的unique_ptr(pu1),这可能导致危害。而#2不会留下悬挂的unique_ptr,因为它调用unique_ptr的构造函数,该构造函数创建的临时对象在其所有权让给pu3后就会被销毁。
注:如果确实想要执行类似与#1的操作,要安全的重用这种指针,可以给它赋新值。C++有一个标准库函数std::move(),能够将一个unique_ptr赋给另一个。例如:
unique_ptr ps1,ps2;
ps1 = unique_ptr(new string(“hello”));
ps2 = move(ps1);
ps1 = unique_ptr(new string(“alexia”));
cout << *ps1 << *ps2 << endl;

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

4**.weak_ptr**(用于解决shared_ptr造成的循环引用)
weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,它的构造不会引起指针引用计数的增加。weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。
例如:

class CB;
class CA
{
public:
    CA() { cout << "CA() called! " << endl; }
    ~CA() { cout << "~CA() called! " << endl; }
    void set_ptr(shared_ptr<CB>& ptr) { m_ptr_b = ptr; }
    void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
    void show() { cout << "this is class CA!" << endl; }
private:
    shared_ptr<CB> m_ptr_b;
};

class CB
{
public:
    CB() { cout << "CB() called! " << endl; }
    ~CB() { cout << "~CB() called! " << endl; }
    void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }
    void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
    void show() { cout << "this is class CB!" << endl; }
private:
    shared_ptr<CA> m_ptr_a;
};

void test_refer_to_each_other()
{
    shared_ptr<CA> ptr_a(new CA());
    shared_ptr<CB> ptr_b(new CB());

    cout << "a use count : " << ptr_a.use_count() << endl;
    cout << "b use count : " << ptr_b.use_count() << endl;

    ptr_a->set_ptr(ptr_b);
    ptr_b->set_ptr(ptr_a);

    cout << "a use count : " << ptr_a.use_count() << endl;
    cout << "b use count : " << ptr_b.use_count() << endl;
}

通过结果可以看到,最后CA和CB的对象并没有被析构,其中的引用效果如下图所示,起初定义完ptr_a和ptr_b时,只有①③两条引用,然后调用函数set_ptr后又增加了②④两条引用,当test_refer_to_each_other这个函数返回时,对象ptr_a和ptr_b被销毁,也就是①③两条引用会被断开,但是②④两条引用依然存在,每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构,造成内存泄漏。

解决这种状况的办法就是将两个类中的一个成员变量改为weak_ptr对象,因为weak_ptr不会增加引用计数,使得引用形不成环,最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB中的成员变量改为weak_ptr对象,代码如下:

class CB
{
public:
    CB() { cout << "CB() called! " << endl; }
    ~CB() { cout << "~CB() called! " << endl; }
    void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }
    void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
    void show() { cout << "this is class CB!" << endl; }
private:
    weak_ptr<CA> m_ptr_a;
};

通过这次结果可以看到,CA和CB的对象都被正常的析构了,引用关系如下图所示,流程与上一例子相似,但是不同的是④这条引用是通过weak_ptr建立的,并不会增加引用计数,也就是说CA的对象只有一个引用计数,而CB的对象只有2个引用计数,当test_refer_to_each_other这个函数返回时,对象ptr_a和ptr_b被销毁,也就是①③两条引用会被断开,此时CA对象的引用计数会减为0,对象被销毁,其内部的m_ptr_b成员变量也会被析构,导致CB对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。


作者:AlbertS
来源:CSDN
原文:https://blog.csdn.net/albertsh/article/details/82286999
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值