C++ 11 智能指针浅析

定义

为了实现指针自动回收的对象,表现和指针一样,实际上它利用了栈的机制,每一个智能指针都是一个模板类,调用智能指针实际上是创建了一个智能指针的对象,对象生命周期到达尽头的时候,会自动调用智能指针的析构函数,在析构函数里,释放掉它管理的内存,从而避免手动delete。

Java里有类似实现(jdk1.2以后有strong,soft,weak,phantom引用)

C++11之前有auto_ptr,但是与STL不兼容,C++11时被弃用

shared_ptr是引用计数的智能指针,而unique_ptr不是。这意味着,可以有多个shared_ptr实例指向同一块动态分配的内存,当最后一个shared_ptr离开作用域时,才会释放这块内存。shared_ptr也是线程安全的。另一方面,unique_ptr意味着所有权。单个unique_ptr离开作用域时,会立即释放底层内存。

默认的智能指针应该是unique_ptr。只有需要共享资源时,才使用shared_ptr,weak_ptr是shared_ptr的弱引用,不增加其引用计数器。

指针简要描述
shared_ptr允许多个指针指向同一个对象
unique_ptr独占所指向的对象
weak_ptrshared_ptr的弱引用

share_ptr

允许多个shared_ptr指向同一个对象,使用引用计数器来判断是否需要释放。访问时像C++一般的指针一样。

最安全和高效的方法是调用make_shared库函数,该函数会在堆中分配一个对象并初始化,最后返回指向此对象的share_ptr实例。如果你不想使用make_ptr,也可以先明确new出一个对象,然后把其原始指针传递给share_ptr的构造函数。

可以*pInt也可以pInt->。指针的拷贝p=q会使q的引用计数++,p的–,如下:

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

class Example
{
public:
    Example() : e(1) { cout << "Example Constructor..." << endl; }
    ~Example() { cout << "Example Destructor..." << endl; }

    int e;
};

int main() {

    shared_ptr<Example> pInt(new Example());
    shared_ptr<Example> pInt3=make_shard<Example>();
    auto pInt4=pInt3;
    cout << (*pInt).e << endl;
    cout << "pInt引用计数: " << pInt.use_count() << endl;

    shared_ptr<Example> pInt2 = pInt;
    cout << "pInt引用计数: " << pInt.use_count() << endl;
    cout << "pInt2引用计数: " << pInt2.use_count() << endl;
    cout << "pInt3引用计数: " << pInt3.use_count() << endl;
    cout << "pInt4引用计数: " << pInt4.use_count() << endl;

    pInt3=pInt;
    cout << "pInt引用计数: " << pInt.use_count() << endl;
    cout << "pInt2引用计数: " << pInt2.use_count() << endl;
    cout << "pInt3引用计数: " << pInt3.use_count() << endl;
    cout << "pInt4引用计数: " << pInt4.use_count() << endl;

}

Example Constructor...                                                        
Example Constructor...                                                          
1                                                                          
pInt引用计数: 1                                                                 
pInt引用计数: 2                                                           
pInt2引用计数: 2                                                     
pInt3引用计数: 2                                                       
pInt4引用计数: 2                                                        
pInt引用计数: 3                                            
pInt2引用计数: 3                                               
pInt3引用计数: 3                          
pInt4引用计数: 1                                                                             
Example Destructor...                                                                          
Example Destructor...   

shared_ptr提供了两个函数来检查其共享的引用计数值,分别是unique()和use_count()。

在前面,我们已经多次使用过use_count()函数,该函数返回当前指针的引用计数值。值得注意的是use_count()函数可能效率很低,应该只把它用于测试或调试。

unique()函数用来测试该shared_ptr是否是原始指针唯一拥有者,也就是use_count()的返回值为1时返回true,否则返回false。

unique_ptr

与shared_ptr不同,某一时刻,只能有一个unique_ptr指向一个给定的对象。因此,当unique_ptr被销毁,它所指的对象也会被销毁。unique_ptr没有类似make_shared函数只能通过unique_ptr<int> pInt(new int(5)); 无法复制构造和赋值。但是可以从函数中返回。也可以同过指针操作。类似auto_ptr

可以进行移动构造和移动赋值操作,通过std::move()函数。

int main() {
    unique_ptr<int> pInt(new int(5));
    unique_ptr<int> pInt2 = std::move(pInt);    // 转移所有权
    //cout << *pInt << endl; // 出错,pInt为空
    cout << *pInt2 << endl;
    unique_ptr<int> pInt3(std::move(pInt2));
}
  • 常见场景:

    • 为动态申请的资源提供异常安全保证
    void Func()
    {
        unique_ptr<int> p(new int(5));
    
        // ...(可能会抛出异常)
    }
    • 返回函数内动态申请资源的所有权
    unique_ptr<int> Func(int p)
    {
      unique_ptr<int> pInt(new int(p));
      return pInt;    // 返回unique_ptr
    }
    
    int main() {
      int p = 5;
      unique_ptr<int> ret = Func(p);
      cout << *ret << endl;
      // 函数结束后,自动释放资源
    }
    • 在容器中保存指针
    int main() {
      vector<unique_ptr<int>> vec;
      unique_ptr<int> p(new int(5));
      vec.push_back(std::move(p));    // 使用移动语义
    }
    • 管理动态数组
    int main() {
      unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5});
      p[0] = 0;   // 重载了operator[]
    }
    • 作为auto_ptr的代替品

weak_ptr

使用计数器的shared_ptr很容易就产生循环引用的问题。这时如果用一个weak_ptr来指向shared_ptr不会增加他的引用计数器。可以使用lock()函数来安全地获取一个weak_ptr的一个shared_ptr的引用,如果此时shared_ptr已被reset,则返回一个空的shared_ptr,赋值语句不成功。也可以安全地通过expired()函数来判断是否过期。

使用时不能通过shared_ptr传统指针操作,但是可以通过lock()来获取他的share_ptr()。可用来解决循环引用的问题。

class ClassB;

class ClassA
{
public:
    ClassA() { cout << "ClassA Constructor..." << endl; }
    ~ClassA() { cout << "ClassA Destructor..." << endl; }
    weak_ptr<ClassB> pb;  // 在A中引用B
};

class ClassB
{
public:
    ClassB() { cout << "ClassB Constructor..." << endl; }
    ~ClassB() { cout << "ClassB Destructor..." << endl; }
    weak_ptr<ClassA> pa;  // 在B中引用A
};

int main() {
    shared_ptr<ClassA> spa = make_shared<ClassA>();
    shared_ptr<ClassB> spb = make_shared<ClassB>();
    spa->pb = spb;
    spb->pa = spa;
    // 函数结束,思考一下:spa和spb会释放资源么?
}

ClassA Constructor...
ClassB Constructor...
ClassA Destructor...
ClassB Destructor...
Program ended with exit code: 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值