在你的代码中使用Boost智能指针
Smart Pointers to boost your code(By peterchen)
翻译 masterlee
Download source files - 45.3kb
正文
智能指针能够使C++的开发简单化,主要是它能够像其它限制性语言(如C#、VB)自动管理内存的释放,而且能够做更多的事情。
1、 什么是智能指针
智能指针是一种像指针的C++对象,但它能够在对象不使用的时候自己销毁掉。
我们知道在C++中的对象不再使用是很难定义的,因此C++中的资源管理是很复杂的。各种智能指针能够操作不同的情况。当然,智能指针能够在任务结束的时候删除对象,除了在程序之外。
许多库都提供了智能指针的操作,但都有自己的优点和缺点。Boost库是一个高质量的开源的C++模板库,很多人都考虑将其加入下一个C++标准库的版本中。
Boost提供了下面几种智能指针:
shared_ptr<T>
本指针中有一个引用指针记数器,表示类型T的对象是否已经不再使用。shared_ptr 是Boost中提供普通的智能指针,大多数地方都使用shared_ptr。
scoped_ptr<T>
当离开作用域能够自动释放的指针。因为它是不传递所有权的。事实上它明确禁止任何想要这样做的企图!这在你需要确保指针任何时候只有一个拥有者时的任何一种情境下都是非常重要的。
intrusive_ptr<T>
比 shared_ptr 更好的智能指针,但是需要类型 T 提供自己的指针使用引用记数机制。
weak_ptr<T>
一个弱指针,帮助shared_ptr 避免循环引用。
shared_array<T>
和 shared_ptr 类似,用来处理数组的。
scoped_array<T>
和 scoped_ptr 类似,用类处理数组的。
下面让我们看一个简单的例子:
2、 首先介绍:boost::scoped_ptr<T>
scoped_ptr 是 Boost 提供的一个简单的智能指针,它能够保证在离开作用域后对象被释放。
例子说明:本例子使用了一个帮助我们理解的类: CSample, 在类的构造函数、赋值函数、析构函数中都加入了打印调试语句。因此在程序执行的每一步都会打印调试信息。在例子的目录里已经包含了程序中需要的Boost库的部分内容,不需要下载其它内容(查看Boost的安装指南)。
下面的例子就是使用scoped_ptr 指针来自动释放对象的:
使用普通指针
使用scoped_ptr 指针
void Sample1_Plain()
{
CSample * pSample(new CSample);
if (!pSample->Query() )
// just some function...
{
delete pSample;
return;
}
pSample->Use();
delete pSample;
}
#include "boost/smart_ptr.h"
void Sample1_ScopedPtr()
{
boost::scoped_ptr<CSample>
samplePtr(new CSample);
if (!samplePtr->Query() )
// just some function...
return;
samplePtr->Use();
}
使用普通普通指针的时候,我们必须记住在函数退出的时候要释放在这个函数内创建的对象。当我们使用例外的时候处理指针是特别烦人的事情(容易忘记销毁它)。使用scoped_ptr 指针就能够在函数结束的时候自动销毁它,但对于函数外创建的指针就无能为力了。
优点:对于在复杂的函数种,使用scoped_ptr 指针能够帮助我们处理那些容易忘记释放的对象。也因此在调试模式下如果使用了空指针,就会出现一个断言。
优点
自动释放本地对象和成员变量[1],延迟实例化,操作PIMPL和RAII(看下面)
缺点
在STL容器里,多个指针操作一个对象的时候需要注意。
性能
使用scoped_ptr 指针,会增加一个普通指针。
3、 引用指针计数器
引用指针计数器记录有多少个引用指针指向同一个对象,如果最后一个引用指针被销毁的时候,那么就销毁对象本身。
shared_ptr 就是Boost中普通的引用指针计数器,它表示可以有多个指针指向同一个对象,看下面的例子:
void Sample2_Shared()
{
// (A) 创建Csample类的一个实例和一个引用。
boost::shared_ptr<CSample> mySample(new CSample);
printf("The Sample now has %i references\n", mySample.use_count()); // The Sample now has 1 references
// (B) 付第二个指针给它。
boost::shared_ptr<CSample> mySample2 = mySample; // 现在是两个引用指针。
printf("The Sample now has %i references\n", mySample.use_count());
// (C) 设置第一个指针为空。
mySample.reset();
printf("The Sample now has %i references\n", mySample2.use_count()); // 一个引用
// 当mySample2离开作用域的时候,对象只有一个引用的时候自动被删除。
}
在(A)中在堆栈重创建了CSample类的一个实例,并且分配了一个shared_ptr指针。对象mySample入下图所示:
然后我们分配了第二个指针mySample2,现在有两个指针访问同一个数据。
我们重置第一个指针(将mySample设置为空),程序中仍然有一个Csample实例,mySample2有一个引用指针。
只要当最有一个引用指针mySample2退出了它的作用域之外,Csample这个实例才被销毁。
当然,并不仅限于单个Csample这个实例,或者是两个指针,一个函数,下面是用shared_ptr的实例:
· 用作容器中。
· 用在PIMPL的惯用手法 (the pointer-to-implementation idiom )。
· RAII(Resource-Acquisition-Is-Initialization)的惯用手法中。
· 执行分割接口。
注意:如果你没有听说过PIMPL (a.k.a. handle/body) 和 RAII,可以找一个好的C++书,在C++中处于重要的内容,一般C++程序员都应该知道(不过我就是第一次看到这个写法)。智能指针只是一中方便的他们的方法,本文中不讨论他们的内容。
PIMPL:如果必须包容一个可能抛异常的子对象,但仍然不想从你自己的构造函数中抛出异常,考虑使用被叫做Handle Class或Pimpl的方法(“Pimpl”个双关语:pImpl或“pointer to implementation”)
4、 主要特点
boost::shared_ptr 有一些重要的特征必须建立在其它操作之上。
· shared_ptr<T>作用在一个未知类型上
当声明或定义一个shared_ptr<T>,T可能是一个未知的类型。例如你仅仅在前面声明了class T,但并没有定义class T。当我们要释放这个指针的时候我们需要知道这个T具体是一个声明类型。
· shared_ptr<T>作用在任意类型上
在这里本质上不需要制定T的类型(如从一个基类继承下来的)
· shared_ptr<T>支持自己定义释放对象的操作
如果你的类中自己写了释放方法,也可以使用。具体参照Boost文档。
· 强制转换
如果你定义了一个U*能够强制转换到T*(因为T是U的基类),那么shared_ptr<U>也能够强制转换到shared_ptr<T>。
· shared_ptr 是线程安全的
(这种设计的选择超过它的优点,在多线程情况下是非常必要的)
· 已经作为一种惯例,用在很多平台上,被证明和认同的。
郑重声明:
允许复制、修改、传递或其它行为
但不准用于任何商业用途.
写于 2004/11/5 masterlee
[1]、 使用scoped_ptr 指针比使用 std::auto_ptr 更能表示出指针的范围和所有权,不会混淆,或者搞得更复杂。