C++智能指针 auto_ptr、shared_ptr、weak_ptr和unique_ptr

一、auto_ptr

  auto_ptr这是C++98标准下的智能指针,现在常常已经被C++标准的其他智能指针取代。它的缺点是在转移所有权后会使运行期不安全。C++11新标准,用unique_ptr来代替auto_ptr原有功能。

auto_ptr <double> pd;
double *p_reg = new double;
pd = p_reg; // 不允许
pd = auto_ptr <double> (p_reg); //允许
auto_ptr <double> panto =p_reg; //不允许
auto_ptr <double> pauto (p_reg); //允许

二、share_ptr

  share_ptr是C++11新添加的智能指针,它限定的资源可以被多个指针共享。

       只有指向动态分配的对象的指针才能交给 shared_ptr 对象托管。将指向普通局部变量、全局变量的指针交给 shared_ptr 托管,编译时不会有问题,但程序运行时会出错,因为不能析构一个并没有指向动态分配的内存空间的指针。

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

void fun() {
    shared_ptr<string> pa(new string("CHN"));
    shared_ptr<string> pb(new string("USA"));
    cout << "*pa " << *pa << endl;//CHN
    cout << "pa.use_count " << pa.use_count() << endl;//1
    cout << "*pb " << *pb << endl;//USA
    cout << "pb.use_count " << pb.use_count() << endl;//1

    pa = pb;
    cout << *pa << endl;//USA
    cout << "pa.use_count " << pa.use_count() << endl;//2:pa和pb指向同一个资源USA了,该资源的计数为2,所以pb、pb都输出2
    cout << "pb.use_count " << pb.use_count() << endl;//2

    pa.reset();
    pb.reset();
    cout << "pa.use_count " << pa.use_count() << endl;//0
    cout << "pb.use_count " << pb.use_count() << endl;//0
}

int main()
{
    fun();
    system("pause");
    return 0;
}

输出结果:

*pa CHN
pa.use_count 1
*pb USA
pb.use_count 1
USA
pa.use_count 2
pb.use_count 2
pa.use_count 0
pb.use_count 0

注意,不能用下面的方式使得两个 shared_ptr 对象托管同一个指针:

A* p = new A(10);

shared_ptr <A> sp1(p), sp2(p);

sp1 和 sp2 并不会共享同一个对 p 的托管计数,而是各自将对 p 的托管计数都记为 1(sp2 无法知道 p 已经被 sp1 托管过)。这样,当 sp1 消亡时要析构 p,sp2 消亡时要再次析构 p,这会导致程序崩溃。

 三、weak_ptr

  weak_ptr是一种用于解决shared_ptr相互引用时产生死锁问题的智能指针。如果有两个shared_ptr相互引用,那么这两个shared_ptr指针的引用计数永远不会下降为0,资源永远不会释放。weak_ptr是对对象的一种弱引用,它不会增加对象的use_count,weak_ptr和shared_ptr可以相互转化,shared_ptr可以直接赋值给weak_ptr,weak_ptr也可以通过调用lock函数来获得shared_ptr。

  1. weak_ptr指针通常不单独使用,只能和 shared_ptr 类型指针搭配使用。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放。
  2. weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。

 

  先看一下两个shared_ptr指针互相引用导致的资源释放失败的例子:

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

class B;
class A
{
public:
    shared_ptr<B> pb_;
    ~A()
    {
        cout << "A delete\n";
    }
};
class B
{
public:
    shared_ptr<A> pa_;
    ~B()
    {
        cout << "B delete\n";
    }
};

void fun() {
    shared_ptr<B> pb(new B());
    cout << "pb.use_count " << pb.use_count() << endl;//1
    shared_ptr<A> pa(new A());
    cout << "pa.use_count " << pa.use_count() << endl;//1

    pb->pa_ = pa;
    cout << "pb.use_count " << pb.use_count() << endl;//1
    cout << "pa.use_count " << pa.use_count() << endl;//2
    pa->pb_ = pb;
    //由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1
    cout << "pb.use_count " << pb.use_count() << endl;//2
    cout << "pa.use_count " << pa.use_count() << endl;//2
}//程序结束时,没有调用A和B的析构函数

int main()
{
    fun();
    system("pause");
    return 0;
}

输出结果:

pb.use_count 1
pa.use_count 1
pb.use_count 1
pa.use_count 2
pb.use_count 2
pa.use_count 2

  

而使用weak_ptr:把A中的shared_ptr<B> pb_改为weak_ptr<B> pb_weak,这样改为了弱引用,传递时不会增加pb引用计数use_count()的值,所以最终能够使A、B资源正常释放:

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

class B;
class A
{
public:
    weak_ptr<B> pb_weak;
    ~A()
    {
        cout << "A delete\n";
    }
};
class B
{
public:
    shared_ptr<A> pa_;
    ~B()
    {
        cout << "B delete\n";
    }
    void print() {
        cout << "This is B" << endl;
    }
};

void fun() {
    shared_ptr<B> pb(new B());
    cout << "pb.use_count " << pb.use_count() << endl;//1
    shared_ptr<A> pa(new A());
    cout << "pa.use_count " << pa.use_count() << endl;//1

    pb->pa_ = pa;
    cout << "pb.use_count " << pb.use_count() << endl;//1
    cout << "pa.use_count " << pa.use_count() << endl;//2

    pa->pb_weak = pb;
    cout << "pb.use_count " << pb.use_count() << endl;//1:弱引用不会增加所指资源的引用计数use_count()的值
    cout << "pa.use_count " << pa.use_count() << endl;//2

    shared_ptr<B> p = pa->pb_weak.lock();
    p->print();//不能通过weak_ptr直接访问对象的方法,须先转化为shared_ptr
    cout << "pb.use_count " << pb.use_count() << endl;//2
    cout << "pa.use_count " << pa.use_count() << endl;//2
}//函数结束时,调用A和B的析构函数

//资源B的引用计数一直就只有1,当pb析构时,B的计数减一,变为0,B得到释放,
//B释放的同时也会使A的计数减一,同时pa自己析构时也会使资源A的计数减一,那么A的计数为0,A得到释放。


int main()
{
    fun();
    system("pause");
    return 0;
}

输出结果:

pb.use_count 1
pa.use_count 1
pb.use_count 1
pa.use_count 2
pb.use_count 1
pa.use_count 2
This is B
pb.use_count 2
pa.use_count 2
B delete
A delete

四、unique_ptr

  unique_ptr 是一个独享所有权的智能指针,它提供了严格意义上的所有权。它取代了C++98中的auto_ptr。

unique_ptr对象包装一个原始指针,并负责其生命周期。当该对象被销毁时,会在其析构函数中删除关联的原始指针。

unique_ptr具有->和*运算符重载符,因此它可以像普通指针一样使用。

不管函数正常退出还是异常退出(由于某些异常),也会始终调用taskPtr的析构函数。因此,原始指针将始终被删除并防止内存泄漏。

 

用法和auto_ptr类似,详情见一下代码:

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

struct Task {
    int mId;
    Task(int id) :mId(id) {
        cout << "Task::Constructor" << endl;
    }
    ~Task() {
        cout << "Task::Destructor" << endl;
    }
};

int main()
{
    // 通过原始指针创建 unique_ptr 实例
    //shared_ptr<Task> taskPtr(new Task(23));
    //int id = taskPtr->mId;

    //调用其lock函数来获得shared_ptr示例,进而访问原始对象。
    //weak_ptr<Task> weak1 = taskPtr;
    //int id = weak1.lock()->mId;

    unique_ptr<Task> taskPtr(new Task(23));
    int id = taskPtr->mId;

    cout << id << endl;

    return 0;
}

输出结果:

Task::Constructor
23
Task::Destructor

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值