用于管理new动态分配对象的智能指针

来源:互联网 发布:win10网络重置后怎么办 编辑:程序博客网 时间:2024/04/28 22:50

一、scoped_ptr

scoped_ptr可以代理一个普通指针,使用时就象使用一个普通指针那样,因为它
1. 提供了隐式转换(operator unspecified_bool_type() const 实现方式见下面的代码)可用于布尔表达式的类型的方法,if ( p ) 判断是否持有一个有效指针(非空指针)
2. 可以象使用裸指针那样调用被指物的成员函数,因为重载了operator->.
3. 也可以和裸指针一样解引用scoped_ptr,因为重载了operator*。

typedef T * this_type::*unspecified_bool_type;  operator unspecified_bool_type() const // 提供bool上下文的类型转换以判断指针的有效性(是否为空){    return ptr == 0? 0: &this_type::ptr;  }  
执行if (ptr)时候便会执行operator unspecified_bool_type() const,转换成判断unspecified_bool_type类型的指针是否为空。返回scoped_ptr是否为非空。返回值的类型是未指明的,但这个类型可被用于Boolean的上下文中。在if语句中最好使用这个类型转换函数,而不要用get去测试scoped_ptr的有效性

scoped_ptr类把拷贝构造函数和赋值构造函数都声明为私有成员,禁止拷贝和赋值,scoped_ptr永远不能被复制或被赋值!scoped_ptr 拥有它所指向的资源的所有权,并永远不会放弃这个所有权(必要时可以通过reset()来放弃或重新获取),不会转让这个所有权,只能在声明的作用域内使用,不可转让。

auto_ptr既可以复制构造也可以赋值,但这们同时也意味着它把所有权从p_auto 转移给了p_another_auto, 在赋值后p_auto将只剩下一个空指针,意味着失去了管理权

scoped_ptr和auto_ptr都不能用作容器的元素,但各自的原因不同:
auto_ptr是因为其“控制权可转移性”;scoped_ptr是应为其不支持拷贝和赋值操作,不符合容器对元素的要求

由于scoped_ptr::get()会返回一个裸指针,所以就有可能对scoped_ptr做一些有害的事情,其中有两件是你尤其要避免的。(防止重复释放同一份内存
1. 不要删除这个裸指针。因为它会在scoped_ptr被销毁时再一次被删除。
2. 不要把这个裸指针保存到另一个scoped_ptr (或其它任何的智能指针)里。因为这样也会两次删除这个指针,每个scoped_ptr一次。
简单地说,尽量少用get, 除非你要使用那些要求你传送裸指针的遗留代码,比如C代码!

如果你想说一个资源是要被限制在作用域里的,并且不应该有办法可以放弃它的所有权,你就应该用boost::scoped_ptr.要决定使用std::auto_ptr还是boost::scoped_ptr, 就要考虑转移所有权是不是你想要的智能指针的一个特性。如果不是,就用scoped_ptr. 它是一种轻量级的智能指针;使用它不会使你的程序变大或变慢。它只会让你的代码更安全,更好维护。

(以下是别人博客里的总结,非常好,转自http://blog.csdn.net/gxp/article/details/6752691?locationNum=15&fps=1)
参考链接:【智能指针 scoped_ptr】

总结
使用裸指针来写异常安全和无错误的代码是很复杂的。使用智能指针来自动地把动态分配对象的生存期限制在一个明确的范围之内,是解决这种问题的一个有效方法,并且提高了代码的可读性、可维护性和质量。scoped_ptr明确地表示被指物不能被共享和转移。正如你所看到的,std::auto_ptr可以从另一个auto_ptr那里窃取被指物,那怕是无意的,这被认为是auto_ptr的最大缺点。正是这个缺点使得scoped_ptr成为auto_ptr最好的补充。当一个动态分配的对象被传送给scoped_ptr, 它就成为了这个对象的唯一的拥有者。因为scoped_ptr几乎总是以自动变量或数据成员来分配的,因此它可以在离开作用域时正确地销毁对象,从而在执行流由于返回语句或异常抛出而离开作用域时,也总能释放它所管理的内存。
在以下情况时使用 scoped_ptr :
1. 在可能有异常抛出的作用域里使用指针
2. 函数里有几条控制路径
3. 动态分配对象的生存期应被限制于特定的作用域内
4. 异常安全非常重要时(总应如此!)

二、shared_ptr

下面,引出有关智能指针中最有价值最重要,最有价值最重要,最有价值最重要的boost::shared_ptr, 引用计数型共享对象的智能指针,弥补了auto_ptr是因为其“控制权可转移性”不能作为STL容器元素的缺陷。
但没有提供算术操作(sp++等)
关于shared_ptr使用规范:
1、避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放 (double delet)

2、shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。

循环引用就是指,两个shared_ptr,你持有我,我持有你,双方都认为对方死了自己才能销毁,结果两方都无法销毁,造成内存泄漏。

3、不要构造一个临时的shared_ptr作为函数的参数。

process(std::shared_ptr<int>(new int(10)), priority());

编译器没有规定传入参数的调用顺序,可能先执行new表达式,然后执行priority()函数,最后构造shared_ptr对象。如果priority()函数中抛出异常,那么new出来的内存将得不到释放。这会造成内存泄漏。
所以要使用下面两种方式:
方式一:

shared_ptr<int> p(new int(10));process(p, priority());

方式二:

process(std::make_shared<int>(10), priority);

前者即是以独立语句将new分配过的对象置入只能指针,后者是利用工厂函数,直接生成只能指针。

make_shared()和普通shared_ptr(new xx)构造方法的区别

make_shared的效率更高。直接使用shared_ptr(new xx)底层实际上涉及两次内存分配,一次是为被管理的对象分配内存,一次是为ref_count控制块分配内存。而make_shared则是一次性分配好一大块内存来同时持有被管理的对象和ref_count控制块。增加了代码执行速度,且能避免一些控制块的薄记信息(内存cookie),潜在减少了程序占用的空间。不过make_shared有个缺点,使用make_shared会导致对象销毁和内存被回收之间可能会有延迟。因为使用make_shared相当于把被管理对象内存和ref_count控制块内存绑定在一起了,我们知道,ref_count控制块内存本来是由use_count和weak_count共同维护的,假设被管理对象引用计数为0了,但是仍然有weak_ptr存在,那么这一大块内存就不能销毁;而传统的shared_ptr(new xx)方式,当对象引用计数为0时,对象内存销毁。当weak_ptr引用计数为0时,ref_count控制块内存销毁,所有的内存都能够及时清理。

shared_ptr的设计体现了代理模式和模板模式。

头文件: "boost/checked_delete.hpp" ~scoped_ptr() {    enum { type_must_be_complete = sizeof(C) }; //用来静态检查不完整的类型(即类型未定义,大小为0)    delete ptr_;  }

通过指针来删除一个对象时,执行的结果取决于执行删除时被删除的类型是否可知。对一个指向不完整类型的指针执行delete几乎不可能有编译器警告,这会导致各种各样的麻烦,由于析构函数可以没有被执行。换句话说,即进行清除的代码没有被执行。checked_delete 在对象析构时执行一个静态断言,测试类是否可知,以确保析构函数被执行。

删除一个动态分配的对象时,必须调用它的析构函数。如果这个类型是不完整的,即只有声明没有定义,那么析构函数可能会没被调用。这是一种潜在的危险状态,所以应该避免它。对于类模板及函数模板,风险会更大,因为无法预先知道会使用什么类型。使用 checked_delete 和 checked_array_delete, 可以解决这个删除不完整类型的问题。它没有运行期的额外开销,只是直接调用 delete, 因此说 checked_delete 带来的安全性实际上是免费的。
如果你需要在调用delete时确保类型是完整的,就使用 checked_delete 。
参考链接:【checked_delete】

这里写图片描述
参考链接:
【shared_ptr源码解读】
【shared_ptr线程安全性分析】
【shared_ptr的理解和注意事项】