智能指针

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

在你的代码中使用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++标准库的版本中。

下面让我们看一个简单的例子:

2、首先介绍:boost::scoped_ptr

scoped_ptr 是 Boost 提供的一个简单的智能指针,它能够保证在离开作用域后对象被释放。

例子说明:本例子使用了一个帮助我们理解的类: CSample, 在类的构造函数、赋值函数、析构函数中都加入了打印调试语句。因此在程序执行的每一步都会打印调试信息。在例子的目录里已经包含了程序中需要的Boost库的部分内容,不需要下载其它内容(查看Boost的安装指南)。

使用普通普通指针的时候,我们必须记住在函数退出的时候要释放在这个函数内创建的对象。当我们使用例外的时候处理指针是特别烦人的事情(容易忘记销毁它)。使用scoped_ptr 指针就能够在函数结束的时候自动销毁它,但对于函数外创建的指针就无能为力了。

优点:对于在复杂的函数种,使用scoped_ptr 指针能够帮助我们处理那些容易忘记释放的对象。也因此在调试模式下如果使用了空指针,就会出现一个断言。

3、引用指针计数器

引用指针计数器记录有多少个引用指针指向同一个对象,如果最后一个引用指针被销毁的时候,那么就销毁对象本身。

shared_ptr 就是Boost中普通的引用指针计数器,它表示可以有多个指针指向同一个对象,看下面的例子:

void Sample2_Shared()

{

// (A)创建Csample类的一个实例和一个引用。

boost::shared_ptr mySample(new CSample);

printf("The Sample now has %i references\n", e_count()); // The

Sample now has 1 references

// (B)付第二个指针给它。

boost::shared_ptr mySample2 = mySample; // 现在是两个引用指针。

printf("The Sample now has %i references\n", e_count());

// (C) 设置第一个指针为空。

mySample.reset();

printf("The Sample now has %i references\n", e_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作用在一个未知类型上

当声明或定义一个shared_ptr,T可能是一个未知的类型。例如你仅仅在前面声明了class T,但并没有定义class T。当我们要释放这个指针的时候我们需要知道这个T具体是一个声明类型。

∙shared_ptr作用在任意类型上

在这里本质上不需要制定T的类型(如从一个基类继承下来的)

∙shared_ptr支持自己定义释放对象的操作

如果你的类中自己写了释放方法,也可以使用。具体参照Boost文档。

∙强制转换

如果你定义了一个U*能够强制转换到T*(因为T是U的基类),那么shared_ptr也能够强制转换到shared_ptr

∙shared_ptr 是线程安全的

(这种设计的选择超过它的优点,在多线程情况下是非常必要的)

∙已经作为一种惯例,用在很多平台上,被证明和认同的。

5、例子:在容器中使用shared_ptr

许多容器类,包括STL,都需要拷贝操作(例如,我们插入一个存在的元素到list,vector,或者container。)当拷贝操作是非常销毁资源的时候(这些操作时必须的),典型的操作就是使用容器指针。

std::vector vec;

vec.push_back( new CMyLargeClass("bigString") );

将内存管理的任务抛给调用者,我们能够使用shared_ptr来实现。

typedef boost::shared_ptr CMyLargeClassPtr;

std::vector vec;

vec.push_back( CMyLargeClassPtr(new CMyLargeClass("bigString")) );

当vector被销毁的时候,这个元素自动被销毁了。当然,除非有另一个智能指针引用了它,则还本能被销毁。让我们看Sample3中的使用:

void Sample3_Container()

{

typedef boost::shared_ptr CSamplePtr;

// (A) create a container of CSample pointers:

std::vector vec;

// (B) add three elements

vec.push_back(CSamplePtr(new CSample));

vec.push_back(CSamplePtr(new CSample));

vec.push_back(CSamplePtr(new CSample));

// (C) "keep" a pointer to the second:

CSamplePtr anElement = vec[1];

// (D) destroy the vector:

vec.clear();