- STL一共给我们提供了4种智能指针:auto_ptr、unique_ptr、sharedptr和weak_ptr(本文档暂不介绍)。
- C++所有的智能指针类都有一个“explicit”构造函数(使其不能使用隐式转换!!),以指针作为参数。比如auto_ptr的类的模板原型为:
因此,不能自动将指针转换为智能指针对象,“必须显示调用”,例子如下:
对于智能指针都应该避免一点:
即:不要将“栈内存的变量”赋值给智能指针!因为这样程序将把delete运算符用于非堆内存,这是错误的!!!
-
将两个指针同时指向一片内存时,如何避免释放资源时多次释放资源的错误。以下例子为这一问题的3种通用的解决方案:
先看以下代码:
此时,vocation和ps两个指针都同时指向一块内存,如何正确释放内存,以下为解决方案:
a. 定义赋值运算符,使其赋值时“深拷贝”。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本,缺点是浪费内存空间,所以智能指针都未采取这种方案。
b. 建立所有权(ownership)概念。对于特定的对象,只能有一个智能指针可拥有,这样只有拥有对象的智能指针的构造函数会删除该对象。“然后让赋值操作转让所有权”。这就是用于auto_ptr和unique_ptr的策略,但unique_ptr策略更为严格。
c. 创建智能更高的指针,跟踪引用特定对象的智能指针计数。这称为“引用计数”。例如,赋值时,计数将加1,而指针过期时,计数将减1。当减为0时才调用delete。这是shared_ptr采用的策略。 -
C++摒弃auto_ptr(C++98)的原因:以以下例子来分析:
上述例子中,运行后会崩溃,因为films[2]已经是空指针了!!
上述例子的auto_ptr转换成shared_ptr或者unique_ptr后,程序就不会崩溃,原因如下:
a. 使用shared_ptr运行时正常,因为shared_ptr采用引用计数,pwin和films[2]都指向同一块内存,在释放空间时因为事先要判断引用计数值得大小因此不会出现多次删除一个对象的错误。
b. 使用unique_ptr时编译出错,与auto_ptr一样,unique_ptr也采用所有权模型,但使用unique_ptr时,程序不会等到运行阶段崩溃,而在编译以下代码时出现错误: 即编译器直接指导你发现潜在的内存错误。
综合所述:摒弃auto_ptr的原因就是:避免潜在的内存崩溃问题!!!! -
unique_ptr优于auto_ptr的原因:
a. 在非法赋值语句的编译阶段就提示错误:
b. 当试图将一个unique_ptr赋值给另一个时,如果源unique_ptr是个临时右值时,编译器允许这么做;如果源unique_ptr将存在一段时间,编译器将禁止这么做。例子如下:
例子一:
例子二:
#1留下悬挂的unique_ptr(pu1),这可能导致危害(即此指针此时其实是无效的,别人可能再次使用它,从而引起错误)。而#2不会留下悬挂unique_ptr,因为它调用unique_ptr的构造函数,该构造函数创建临时对象在其所有权给pu3后就会被销毁。
如果当我们想要执行类似于#1的操作,这时想将pu1指针重用,并可给它赋新值,我们可以使用C++的一个标准库函数std::move(),让你能够将一个unique_ptr赋给另一个。代码演示如下:
使用move后,原来的指针仍转让所有权变成空指针,但可以对其重新赋值!!!
- 如何选智能指针:
a. 如果程序要使用多个指向同一个对象的指针,应该选择shared_ptr。
b. 如果程序不需要多个指向同一个对象的指针,则可以使用unique_ptr。即如果函数使用new内存分配,并返还指向该内存的指针,将其返回类型声明为unique_ptr是个不错的选择。这样,所有权转让给接收返回值得unique_ptr,而该智能指针将负责调用delete。可将unique_ptr存储到STL容器,只要不调用将一个unique_ptr复制或赋给一个算法(如sort())。