5、
例子:在容器中使用
shared_ptr
许多容器类,包括
STL
,都需要拷贝操作(例如,我们插入一个存在的元素到
list,vector,
或者
container
。)当拷贝操作是非常销毁资源的时候(这些操作时必须的),典型的操作就是使用容器指针。
std::vector<CMyLargeClass *> vec;
vec.push_back( new CMyLargeClass("bigString") );
|
将内存管理的任务抛给调用者,我们能够使用
shared_ptr
来实现。
typedef boost::shared_ptr<CMyLargeClass> CMyLargeClassPtr;
std::vector<CMyLargeClassPtr> vec;
vec.push_back( CMyLargeClassPtr(new CMyLargeClass("bigString")) );
|
当
vector
被销毁的时候,这个元素自动被销毁了。当然,除非有另一个智能指针引用了它,则还本能被销毁。让我们看
Sample3
中的使用:
void
Sample3_Container()
{
typedef
boost::shared_ptr<CSample> CSamplePtr;
// (A) create a container of CSample pointers:
std::vector<CSamplePtr> 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();
// (E) the second element still exists
anElement->Use();
printf("done. cleanup is automatic\n");
// (F) anElement goes out of scope, deleting the last CSample instance
}
|
6、
使用
Boost
中的智能指针,什么是正确的使用方法
使用智能指针的一些操作会产生错误(突出的事那些不可用的引用计数器,一些对象太容易释放,或者根本释放不掉)。
Boost
增强了这种安全性,处理了所有潜在存在的危险,所以我们要遵循以下几条规则使我们的代码更加安全。
下面几条规则是你应该必须遵守的:
规则一:赋值和保存
——
对于智能指针来说,赋值是立即创建一个实例,并且保存在那里。现在智能指针拥有一个对象,你不能手动释放它,或者取走它,这将帮助你避免意外地释放了一个对象,但你还在引用它,或者结束一个不可用的引用计数器。
规则二:_ptr<T> 不是T*
——
恰当地说,不能盲目地将一个
T*
和一个智能指针类型
T
相互转换。意思是:
·
当创建一个智能指针的时候需要明确写出
__ptr<T> myPtr<new T>
。
·
不能将
T*
赋值给一个智能指针。
·
不能写
ptr = NULL
,应该使用
ptr.reset()
。
·
重新找回原始指针,使用
ptr.get()
,不必释放这个指针,智能指针会去释放、重置、赋值。使用
get()
仅仅通过函数指针来获取原始指针。
·
不能通过
T*
指向函数指针来代表一个
__ptr<T>
,需要明确构造一个智能指针,或者说将一个原始指针的所有权给一个指针指针。(见规则三)
·
这是一种特殊的方法来认定这个智能指针拥有的原始指针。不过在
Boost:smart pointer programming techniques
举例说明了许多通用的情况。
规则三:非循环引用
——
如果有两个对象引用,而他们彼此都通过一个一个引用指针计数器,那么它们不能释放,
Boost
提供了
weak_ptr
来打破这种循环引用(下面介绍)。
规则四:非临时的
share_ptr
——
不能够造一个临时的
share_ptr
来指向它们的函数,应该命名一个局部变量来实现。(这可以使处理以外更安全,
Boost share_ptr best practices
有详细解说)。
7、
循环引用
引用计数器是一种便利的资源管理机制,它有一个基本回收机制。但循环引用不能够自动回收,计算机很难检测到。一个最简单的例子,如下:
struct
CDad;
struct
CChild;
typedef
boost::shared_ptr<CDad> CDadPtr;
typedef
boost::shared_ptr<CChild> CChildPtr;
struct
CDad : public CSample
{
CChildPtr myBoy;
};
struct
CChild : public CSample
{
CDadPtr myDad;
};
// a "thing" that holds a smart pointer to another "thing":
CDadPtr parent(
new
CDadPtr);
CChildPtr child(
new
CChildPtr);
// deliberately create a circular reference:
parent->myBoy = child;
child->myDad = dad;
// resetting one ptr...
child.reset();
|
parent
仍然引用
CDad
对象,它自己本身又引用
CChild
。整个情况如下图所示:
如果我们调用
dad.reset()
,那么我们两个对象都会失去联系。但这种正确的离开这个引用,共享的指针看上去没有理由去释放那两个对象,我们不能够再访问那两个对象,但那两个对象的确还存在,这是一种非常严重的内存泄露。如果拥有更多的这种对象,那么将由更多的临界资源不能正常释放。
如果不能解决好共享智能指针的这种操作,这将是一个严重的问题(至少是我们不可接受的)。因此我们需要打破这种循环引用,下面有三种方法:
A、
当只剩下最后一个引用的时候需要手动打破循环引用释放对象。
B、
当
Dad
的生存期超过
Child
的生存期的时候,
Child
需要一个普通指针指向
Dad
。
C、
使用
boost::weak_ptr
打破这种循环引用。
方法
A
和
B
并不是一个完美的解决方案,但是可以在不使用
weak_ptr
的情况下让我们使用智能指针,让我们看看
weak_ptr
的详细情况。
8、
使用
weak_ptr
跳出循环
强引用和弱引用的比较:
一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。
boost::share_ptr
就是强引用。相对而言,弱引用当引用的对象活着的时候不一定存在。仅仅是当它存在的时候的一个引用。
boost::weak_ptr<T>
是执行弱引用的智能指针。当你需要它的时候就可以使用一个强(共享)指针指向它(当对象被释放的时候,它为空),当然这个强指针在使用完毕应该立即释放掉,在上面的例子中我们能够修改它为弱指针。
struct CBetterChild : public CSample
{
weak_ptr<CDad> myDad;
void BringBeer()
{
shared_ptr<CDad> strongDad = myDad.lock(); // request a strong pointer
if (strongDad) // is the object still alive?
strongDad->SetBeer();
// strongDad is released when it goes out of scope.
// the object retains the weak pointer
}
};
|
9、
Intrusive_ptr
——轻量级共享智能指针
shared_ptr
比普通指针提供了更完善的功能。有一个小小的代价,那就是一个共享指针比普通指针占用更多的空间,每一个对象都有一个共享指针,这个指针有引用计数器以便于释放。但对于大多数实际情况,这些都是可以忽略不计的。
intrusive_ptr
提供了一个折中的解决方案。它提供了一个轻量级的引用计数器,但必须对象本身已经有了一个对象引用计数器。这并不是坏的想法,当你自己的设计的类中实现智能指针相同的工作,那么一定已经定义了一个引用计数器,这样只需要更少的内存,而且可以提高执行性能。
如果你要使用
intrusive_ptr
指向类型
T
,那么你就需要定义两个函数:
intrusive_ptr_add_ref
和
intrusive_ptr_release
。下面是一个简单的例子解释如何在自己的类中实现:
#include "boost/intrusive_ptr.hpp"
// forward declarations
class CRefCounted;
namespace boost
{
void intrusive_ptr_add_ref(CRefCounted * p);
void intrusive_ptr_release(CRefCounted * p);
};
// My Class
class CRefCounted
{
private:
long references;
friend void ::boost::intrusive_ptr_add_ref(CRefCounted * p);
friend void ::boost::intrusive_ptr_release(CRefCounted * p);
public:
CRefCounted() : references(0) {} // initialize references to 0
};
// class specific addref/release implementation
// the two function overloads must be in the boost namespace on most compilers:
namespace boost
{
inline void intrusive_ptr_add_ref(CRefCounted * p)
{
// increment reference count of object *p
++(p->references);
}
inline void intrusive_ptr_release(CRefCounted * p)
{
// decrement reference count, and delete object when reference count reaches 0
if (--(p->references) == 0)
delete p;
}
} // namespace boost
|
这是一个最简单的(非线程安全)实现操作。但作为一种通用的操作,如果提供一种基类来完成这种操作或许很有使用价值,也许在其他地方会介绍到。
10、
scoped_array
和
shared_array
scoped_array
和
shared_array
和上面讲的
基本上相同,只不过
他们
是指向数组的。就像使用指针操作一样使用
operator new[]
,他们都重载了
operator new[]
。注意他们并不初始化分配长度。
11、
Boost
的安装
从
www.boost.org
上下载最新版本的
boost
,然后解压缩到你指定的目录里,解压缩后的文件目录如下:
Boost\ boost
的源文件和头文件。
Doc\ HTML
格式的文档。
Lib\
库文件(不是必需的)
…
其他文件(“
more\
”里有其他资料)
添加目录到我们自己的
IDE
里:
VC6
:在菜单
Tools/Options
,
Directories tab, "Show Directories for... Include files",
VC7
:
在菜单
Tools/Options, Projects/VC++ directories, "Show Directories for... Include files".
Boost
的头文件都在
boost\
子目录里,例如本文档例子中有
#include "boost/smart_ptr.hpp"
。所以任何人当读到年的源文件的时候就立刻知道你用到了
boost
中的智能指针。
12、
关于本文档中的例子
本文档中的例子里有一个子目录
boost\
仅仅包含了本例子中使用到的一些头文件,仅仅是为了你编译这个例子,如果你需要下载完整的
boost
或者获取更多的资源请到
www.boost.org
。
13、
VC6
中
min/max
的灾难
当在
VC
中使用
boost
库,或者其他库的时候会有一些小的问题。
在
Windows
的头文件中已经定义了
min
和
max
宏,所以在
STL
中的这两个函数就调用不到了,例如在
MFC
中就是这样,但是在
Boost
中,都是使用的
std::
命名空间下的函数,使用
Windows
的函数不能够接受不同类型的参数在模板中使用,但是许多库都依赖这些。
虽然
Boost
尽量处理这些问题,但有时候遇到这样的问题的时候就需要在自己的代码中加入像下面的代码在第一个
#include
前加入
#define _NOMINMAX
。
#define _NOMINMAX // disable windows.h defining min and max as macros
#include "boost/config.hpp" // include boosts compiler-specific "fixes"
using std::min; // makle them globally available
using std::max;
|
这样操作并不是在任何时候都需要,而只有我们碰到使用了就需要加入这段代码。
14、
资源
获取更多的信息,或者有问题可以查找如下资源:
·
Boost home page
·
Download Boost
·
Smart pointer overview
·
Boost users mailing list
·
Boost中的智能指针
(
撰文 Bjorn Karlsson 翻译 曾毅)