智能指针其实也不是完全的指针,应该说是像指针一样的类对象,智能指针通常有指针的功能,当然同时也包含了一些额外的功能。目前比较常见的智能指针有auto_ptr、unique_ptr和shared_ptr三种(其实还有weak_ptr第四种),本篇主要也是讲这三种。
auto_ptr是C++98提出来的,到了C++11基本已经摒弃这种用法,但是一些编译器还没有支持C++11的标准,还用C++98的标准,故而auto_ptr可能还在使用。之所以要用到智能指针是因为在一些情况下,开发者可能声明了一个指针并开辟了内存,但是却忘了释放内存,或者有些情况不是忘了释放内存,而是软件出现异常,释放内存的语句没有被执行到,这些原因都容易造成内存泄漏,这也是智能指针出现的一个原因吧。
1、智能指针的使用
C++的智能指针包含在头文件<memory>中,该头文件中定义了智能指针的函数模板,并且在命名空间std中。在声明一个智能指针的时候要声明数据类型,同时传入开辟的堆内存,这里要强调堆内存,不能是栈内存:
auto_ptr<double> apd(new double); // OK
unique_ptr<double> upd(new double); // OK
shared_ptr<double> spd(new double); // OK
int dNUM = 1;
auto_ptr<double> apd(dNUM); // Error
声明了智能指针后就不需要使用delete语句来释放内存了。
所有的智能指针都有一个explicit构造函数,该函数将指针作为参数,因此不需要自动将指针转换为智能指针对象:
shared_ptr<double> spd;
double *pD = new double;
spd = pD; //Error
spd = shared_ptr<double>(pD); //OK
shared_ptr<double> spd1 = pD; //Error
shared_ptr<double> spd1(pD); //OK
智能指针的很多用法都跟普通指针一样,可以对它进行接触引用操作(*)、用->或者.访问成员,还可以将智能指针对象赋值给另一个同类型的对象,但是由于智能指针对象有自动回收内存功能,所以这样做容易导致同一块内存被释放两次的问题。
2、有关智能指针的三个方式
为了避免同块内存被释放两次的惨剧,可以有以下三种方式:
1、定义对象赋值运算符,使之执行深拷贝,这样两个智能指针指向不同的内存;
2、建立所有权(ownership)概念,对于特定对象,只能有一个指针拥有它,也只有拥有它的指针可以删除它,然后,让赋值操作转让所有权,这种方法是auto_ptr和unique_ptr使用的策略,unique_ptr会比auto_ptr严格;
3、使用引用计数(reference counting)创建更智能的指针,跟踪引用特定对象的智能指针的数量,赋值时计数加一,指针过期时计数减一,仅当计数为0的时候才调用delete语句释放内存,这就是shared_ptr使用的策略。
3、为何unique_ptr优于auto_ptr
1、unique_ptr赋值给同类型的unique_ptr操作的时候,编译会报错,而auto_ptr会等到运行时才报错;将unique_ptr赋值给另一个的时候,如果该unique_ptr是个临时的右值,也就是赋值后该unique_ptr不会被使用到,如在函数中返回unique_ptr,那么编译器是一般会通过的
2、unique_ptr使用new和delete、new[]和delete[]两种配套,但是auto_ptr只使用new和delete,所以unique_ptr可以用于数组的变体,但是auto_ptr不能。
4、选择
1、如果程序中会用到多个智能指针指向同一对象,那么肯定要用shared_ptr,包括:有一个指针数组,并使用了一些辅助指针来标识特定元素,如最大最小值等;两个第项都包含第三个对象的指针;STL容器包含指针;
2、不会用多个指针指向同一对象,则用unique_ptr,可将unique_ptr转换为shared_ptr;
3、不支持新标准(如C++11以上的)的编译器,只能用auto_ptr;
溪水急著要流向海洋
浪潮却渴望重回土地
在绿树白花的篱前
曾那样轻易地挥手道别
而沧桑了二十年後
我们的魂魄却夜夜归来
微风拂过时
便化作满园的郁香