C++11 智能指针

本文根据众多互联网博客内容整理后形成,引用内容的版权归原始作者所有,仅限于学习研究使用,不得用于任何商业用途。

智能指针主要有:unique_ptr, shared_ptr, weak_ptr。这3种指针组件就是采用了boost里的智能指针方案。很多有用过boost智能指针的朋友,很容易地就能发现它们之间的关间:

std boost 功能说明
unique_ptr scoped_ptr 独占指针对象,并保证指针所指对象生命周期与其一致
shared_ptr shared_ptr 共享指针对象,可以赋值给shared_ptr或weak_ptr。指针所指对象在所有的相关联的shared_ptr生命周期结束时结束,是强引用。
weak_ptr weak_ptr 它不能决定所指对象的生命周期,引用所指对象时,需要lock()成shared_ptr才能使用。

C++11将boost里的这一套纳入了标准。
如下为示例代码:

//文件 test-1.cpp
#include <memory>
#include <iostream>
using namespace std;
int main(){
    unique_ptr<int> up1(new int(11));
    unique_ptr<int> up2 = up1;   //! 编译时会出错 [1]

    cout << *up1 << endl;
    unique_ptr<int> up3 = move(up1);  //! [2]
    cout << *up3 << endl;
    if (up1)
        cout << *up1 << endl;

    up3.reset();  //! [3]
    up1.reset();

    shared_ptr<string> sp1(make_shared<string>("Hello"));
    shared_ptr<string> sp2 = sp1;
    cout << "*sp1:" << *sp1 << endl;
    cout << "*sp2:" << *sp2 << endl;
    sp1.reset();
    cout << "*sp2:" << *sp2 << endl;

    weak_ptr<string> wp = sp2; //! [4]
    cout << "*wp.lock():" << *wp.lock() << endl;
    sp2.reset();
    cout << "*wp.lock():" << *wp.lock() << endl;  //! 运行时会出错
    return 0;
}
//编译命令: g++ -std=c++11 test-1.cpp

[1]: unique_ptr是禁止复制赋值的,始终保持一个 unique_ptr管理一个对象。
[2]: unique_ptr虽然不能赋值,但可以通过 move()函数转移对象的所有权。一旦被 move()了,原来的 up1则不再有效了。
[3]: reset()可以让 unique_ptr提前释放指针。
[4]: 由 shared_ptr构造一个 weak_ptr。

unique_ptr
unique_ptr(定义在中)提供了一种严格的语义上的所有权,

  • 拥有它所指向的对象。
  • 无法进行复制构造,也无法进行复制赋值操作(译注:也就是对其无法进行复制,我们无法得到指向同一个对象的两个unique_ptr),但是可以进行移动构造和移动赋值操作。
  • 保存指向某个对象的指针,当它本身被删除释放的时候(例如,离开某个作用域),会使用给定的删除器(deleter)删除释放它指向的对象。

unique_ptr的使用能够包括:

  • 为动态申请的内存提供异常安全
  • 将动态申请内存的所有权传递给某个函数
  • 从某个函数返回动态申请内存的所有权
  • 在容器中保存指针

“所有auto_ptr应该已经具有的(但是我们无法在C++98中实现的)功能”
unique_ptr十分依赖于右值引用和移动语义。
下面是一段传统的会产生不安全的异常的代码:

X* f()
{
    X* p = new X;
    // 做一些事情 – 可能会抛出某个异常
    return p;
}

解决方法是,用一个unique_ptr 来管理这个对象的所有权,由其进行这个对象的删除释放工作:

X* f()
{
    unique_ptr p(new X); // 或者使用{new X},但是不能 = new X
    // 做一些事情 – 可能会抛出异常
    return p.release();
}

现在,如果程序执行过程中抛出了异常,unique_ptr就会(毫无疑问地)删除释放它所指向的对象,这是最基本的RAII。但是,除非我们真的需要返回一个内建的指针,我们可以返回一个unique_ptr,让事情变得更好。

unique_ptr f()
{
    unique_ptr p(new X); // 或者使用{new X},但是不能 = new X
    //做一些事情 – 可能会抛出异常
    return p; // 对象的所有权被传递出f()
}

现在我们可以这样使用函数f():

void g()
{
    unique_ptr q = f(); // 使用移动构造函数(move constructor)
    q->memfct(2); // 使用q
    X x = *q; // 复制指针q所指向的对象
    // …
} // 在函数退出的时候,q以及它所指向的对象都被删除释放

unique_ptr拥有“移动意义(move semantics)”,所以我们可以使用函数f() 返回的右值对q进行初始化,这样就简单地将所有权传递给了q。

在那些要不是为了避免不安全的异常问题(以及为了保证指针所指向的对象都被正确地删除释放),我们不可以使用内建指针的情况下,我们可以在容器中保存unique_ptr以代替内建指针:

vector<unique_ptr<string>> vs { new string{“Doug”},
                                new string{“Adams”} };

unique_ptr可以通过一个简单的内建指针构造完成,并且与内建指针相比,两者在使用上的差别很小。特殊情况下,unique_ptr并不提供任何形式的动态检查(?)。

shared_ptr 与 weak_ptr

std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(“弱”)引用。在访问所引用的对象前必须先转换为 std::shared_ptr。
std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。
此外,std::weak_ptr 还可以用来避免 std::shared_ptr 的循环引用。
如下面的示例:

shared_ptr<string> s1(new string);
shared_ptr<string> s2 = s1;
weak_ptr<string> w1 = s2;

在内存中:



s1, s2, w1 都指向一个 ptr_manage 的对象。
在该对象中有 shared_ref_count与 weak_ref_count两个域分别记录引用它的 shared_pt与 weak_ptr的个数。这个很容易办到,只要在复制构造与赋值函数中对相当地引用值进行加1,在析构中减1即可。ptr_manage中的 ptr域存放真正的对象指针地址。

当 shared_ref_cnt被减为0时,自动释放ptr指针所指向的对象
当 shared_ref_cnt与 weak_ref_cn都变成0时,才释放ptr_manage对象
如此以来,只要有相关联的 shared_ptr存在,对象就存在。weak_ptr不影响对象的生命周期。当用 weak_ptr访问对象时,对象有可能已被释放了,要先 lock()。

当执行:

s1.reset()

此时:



shared_ref_cnt由2减成了1。

再执行:

s2.reset()

此时:



shared_ref_cnt已被减到0了,ptr所对应的object已被释放,ptr被清0。此时,ptr_manage依旧保留。因为 w1还需要引用它。

在最后,w1也析构了的时候:



ptr_manage中的 weak_ref_cnt被减成0,最后连 ptr_manage都释放了。

参考资料
C++11中的智能指针
【c++11FAQ】unique_ptr
std::weak_ptr | cppreference.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容

  • 原作者:Babu_Abdulsalam 本文翻译自CodeProject,转载请注明出处。 引入### Ooops...
    卡巴拉的树阅读 29,942评论 13 74
  • C++智能指针 原文链接:http://blog.csdn.net/xiaohu2022/article/deta...
    小白将阅读 6,799评论 2 21
  • 1. 什么是智能指针? 智能指针是行为类似于指针的类对象,但这种对象还有其他功能。 2. 为什么设计智能指针? 引...
    MinoyJet阅读 619评论 0 1
  • C++ 智能指针详解 一、简介由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 de...
    yangqi916阅读 1,335评论 0 2
  • 导读## 最近在补看《C++ Primer Plus》第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰...
    小敏纸阅读 1,951评论 1 12