c++中的四种智能指针

c++中的四种智能指针

写惯了python,golang再来写c++总觉得头大,很大一个原因就是他没有一个GC机制。

不过c++中提供了智能指针,也不是不能用,李姐万岁!

auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被11弃用。

1.auto_ptr

这个指针采用了所有权的方式,

#include<memory>
#include <iostream>
using namespace std;
int main() {
    auto_ptr<string> p_s1(new string("this is  a string"));
    cout << *p_s1.get() << endl;
    auto_ptr<string> p_s2 = p_s1;
    cout << *p_s2.get() << endl;
    //cout << *p_s1.get() << endl;报错,此时s1已经没有所指向的内存的所有权
}

auto_ptr的缺点是:存在潜在的内存崩溃问题!

2.unique_ptr

unique_ptr同样是独占的,但unique_ptr还有更聪明的地方:当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做。

实在想赋值的话,可以用std::move()实现。

#include<memory>
#include <iostream>
using namespace std;
int main() {
    unique_ptr<string> p_s1(new string("this is  a string"));//临时右值是可以用来赋值的
    cout << *p_s1.get() << endl;
    //unique_ptr<string> p_s2 =p_s1;无法直接赋值
    unique_ptr<string> p_s2 = std::move(p_s1);
    cout << *p_s2.get() << endl;
}

3.shared_ptr

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

shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针,其可以检测到所管理的对象是否已经被释放,从而避免非法访问。

4. weak_ptr

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

class B;
class A
{
public:
    shared_ptr<B> pb_;
    ~A()
    {
        cout << "A delete"<<pb_.use_count()<<endl;
    }
};
class B
{
public:
    shared_ptr<A> pa_;
    ~B()
    {
        cout << "B delete"<<pa_.use_count()<<endl;
    }
};
void fun()
{
    shared_ptr<B> pb(new B());
    shared_ptr<A> pa(new A());
    pb->pa_ = pa;
    pa->pb_ = pb;
    cout << pb.use_count() << endl;
    cout << pa.use_count() << endl;
}
int main()
{
    fun();
    return 0;
}

可以看到fun函数中pa ,pb之间互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A B的析构函数没有被调用),如果把其中一个改为weak_ptr就可以了,我们把类A里面的shared_ptr pb_; 改为weak_ptr pb_; 运行结果如下,

1
2
B delete1
A delete0

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

注意的是我们不能通过weak_ptr直接访问对象的方法,比如B对象中有一个方法print(),我们不能这样访问,pa->pb_->print(); 英文pb_是一个weak_ptr,应该先把它转化为shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();

改后如下

class B;
class A
{
public:
    weak_ptr<B> pb_;
    ~A()
    {
        cout << "A delete"<<pb_.use_count()<<endl;
    }
    
};
class B
{
public:
    shared_ptr<A> pa_;
    ~B()
    {
        cout << "B delete"<<pa_.use_count()<<endl;
    }
    void func() {
        cout << "this is print" << endl;
    }
};
void fun()
{
    
    shared_ptr<A> pa(new A());
    shared_ptr<B> pb(new B());
    pb->pa_ = pa;
    pa->pb_ = pb;
    cout << pb.use_count() << endl;
    cout << pa.use_count() << endl;
    (*(*pa).pb_.lock()).func();
}
int main()
{
    fun();
    return 0;
}

输出如下

1
2
this is print
B delete2
A delete
上一篇:Java弱引用(WeakReference)的理解与使用


下一篇:Java弱引用(WeakReference)的理解与使用