C++中的智能指针

1.auto_ptr,share_ptr概述

在开发过程中,曾经使用过两种C++的智能指针:auto_ptr和shared_ptr如今,便总结一下,顺便比较比较二者使用中的区别,注意避免入坑的危险。
我们知道,在C++中,如果创建一个指向某个对象的指针,那么在使用完这个对象之后我们需要自己delete它。否则,会造成一个悬垂指针(dangling pointer),非常容易造成内存泄漏。事实上,如果在使用指针的同时,程序运行抛出异常,那么所指向的对象仍然不会被安全删除,依然可能会出现内存泄露的危险。与普通的指针相比而言,智能指针的区别在于它实际上是对普通指针加了一层封装机制,这样的一层封装机制的目的是使智能指针可以方便的管理一个对象的生命期。在生命期结束的时候,自动的删除内存中被占用的区域。也就是说,智能指针的出现实际上就是为了可以方便的控制对象的生命期,在智能指针中,一个对象什么时候和在什么条件下要被析构或者是删除是受智能指针本身决定的,用户并不需要管理,以此来增强程序的健壮性。

A.auto_ptr

它是C++标准库提供的一种智能指针,在构造时获取某个对象的所有权,在析构时,释放该对象RAII(Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。
简单的说,RAII的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。
如下例所示,即为auto_ptr的使用规则:

int *p = new int(10);  
std::auto_ptr <int> ap(p);  

但是使用auto_ptr,有一些必须注意的问题:

  • (1)不可以将两个或两个以上的auto_ptr指向同一个对象,因为其中当其中一个auto_ptr的生命期结束时,它就会析构对象,而另一个auto_ptr在析构时,将造成多重析构问题,这是很不安全的内存操作。故而,以下的行为是一定要避免的:
int  *p = new int(0);  
auto_ptr<int> ap1(p);  
auto_ptr<int> ap2(p);  
//ap1和ap2都指向一个对象,这就是极危险的,必须防止这么使用
  • (2)不可使用auto_ptr来管理数组指针
    因为auto_ptr在析构时,使用的是delete,而不是delete[]。所以以下的使用方式是绝对要避免的。
int *pa = new int[10];  
auto_ptr<int> ap(pa);  
//这种写法是完全错误的,极可能引起内存泄露的危险
  • (3)
    auto_ptr强调对“裸”指针的完全占有性。也就是说,一个“裸”指针不能同时被两个以上的“裸”指针所拥有。那么,在拷贝构造函数和赋值中,auto_ptr均采取了“所有权转移”的策略,即原指针将失去对裸指针的所有权。
int *p = new int (10);  
auto_ptr<int> ap1(p);  
auto_ptr<int> ap2 = ap1;  //这会使得ap1变成了NULL
cout << *ap1 <<endl;      //错误!此时,对其解引用是不安全的。

如上的错误相对而言,比较容易避免,但是对于以下的代码,错误就较难发现。

void fun(auto_ptr<int> ap)  
{  
     cout << *ap << endl;  
}  
auto_ptr<int> ap(new int(0) ) ;  
fun(ap);  
cout << *ap << endl;  
//错误!对空指针的解引用

上述情况比较隐蔽,这是因为在函数调用时,传参引起了拷贝构造函数的调用,所以原来的指针失去了其所有权。

  • (4)auto_ptr不具备值语义,所以auto_ptr不能被用在STL容器中。
    所谓值语义:使之符合以下条件的类型,设有类Obj:
Obj a;  
Obj b(a);  
Obj c;  
c = a;  

那么a== b, a == c显然,auto_ptr并不具有这个特点。

  • auto_ptr用法要点:
    A. 需要包含头文件<memory>
    B.Constructor:explicit auto_ptr(X* p = 0) throw(); 将指针p交给auto_ptr对象托管。
    C.Copy constructor:auto_ptr(constauto_ptr&) throw(); template<class Y> auto_ptr(constauto_ptr<Y>& a) throw(); 指针的托管权会发生转移。
    D.Destructor: ~auto_ptr(); 释放指针p指向的空间。
    E. 提供了两个成员函数 Xget() const throw(); //返回保存的指针
    F. 对象中仍保留指针 X
    release() const throw(); //返回保存的指针,对象中不保留指针

  • auto_ptr实现关键点:
    A.利用特点“栈上对象在离开作用范围时会自动析构”。
    B.对于动态分配的内存,其作用范围是程序员手动控制的,这给程序员带来了方便但也不可避免疏忽造成的内存泄漏,毕竟只有编译器是最可靠的。
    C.auto_ptr通过在栈上构建一个对象a,对象a中wrap了动态分配内存的指针p,所有对指针p的操作都转为对对象a的操作。而在a的析构函数中会自动释放p的空间,而该析构函数是编译器自动调用的,无需程序员操心。

B.shared_ptr

鉴于auto_ptr所出现的无法复制,且不能满足标准容器对元素的要求,所以boost库中提供了一种新型的智能指针shared_ptr,它通过引用计数(reference counting)的原理,解决了多个指针之间共享对象所有权的问题,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。 shared_ptr也可以安全地放到标准容器中,并弥补了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷。

在C++11中,shared_ptr在<memory>中被定义为: template<class T > class shared_ptr;
std::shared_ptr是通过指针保持某个对象的共享拥有权的智能指针。若干个shared_ptr对象可以拥有同一个对象;最后一个指向该对象的shared_ptr被销毁或重置时,该对象被销毁。销毁该对象时使用的是delete表达式或者是在构造shared_ptr时传入的自定义删除器(deleter)。
shared_ptr也可以不拥有对象,称作空(empt))。 shared_ptr满足CopyConstructible和CopyAssignable的要求。

  • shared_ptr实现细节
成员类型 定义
element_type T The type of the managed object 即被shared_ptr管理着的对象
  • 包含的成员函数如下所示:
成员函数 成员函数
(constructor) constructsnew shared_ptr
(destructor) 如果没有更多shared_ptrs的的链接解构拥有的对象
operator= 分配shared_ptr
reset 取代管理的对象
swap 交换所管理的对象
get 返回一个指针,指向被管理对象
operator* /operator-> 解引用指针到的管理对象
use_count shared_ptr对象指的是在同一个管理对象的数量
unique 检查是否被管理对象的管理仅由当前shared_ptr的实例
operator bool 检查是否有相关的管理对象
owner_before Owner-based ordering

智能指针share_ptr的实现基于引用计数实现,有机会分析一个引用计数的原理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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++ Primer Plus》第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰...
    小敏纸阅读 1,951评论 1 12
  • 1. 什么是智能指针? 智能指针是行为类似于指针的类对象,但这种对象还有其他功能。 2. 为什么设计智能指针? 引...
    MinoyJet阅读 619评论 0 1
  • 内存泄露(臭名昭著的BUG)动态申请堆空间,用完后不归还C++语言中没有垃圾回收的机制指针无法控制所指堆空间的生命...
    nethanhan阅读 310评论 0 0