网站公告列表

  没有公告

加入收藏
设为首页
联系站长
您现在的位置: 网络学院 >> 程序设计 >> VC编程 >> 文章正文
  智能指针的标准之争:Boost vs. Loki            【字体:
智能指针的标准之争:Boost vs. Loki
作者:佚名    文章来源:不详    点击数:    更新时间:2007-9-12    

智能指针的标准之争:Boost vs. Loki

撰文/马维达 weida@flyingdonkey.com

2001 10 月和2002 4 月,在美国的华盛顿和荷兰的安的列斯群岛上分别召开了两次C++标准会议。
正在装载数据……

议的内容之一是对一项新的C++特性提议——智能指针(Smart Pointer——进行讨论。本文将对可能成为C

++新标准的两种智能指针方案(Boost vs. Loki)进行介绍和分析,并给出了相应的使用实例。

关键词:智能指针 C++ Boost Loki

在现在的标准C++中,只有一种智能指针:std::auto_ptr。其原因并非是因为auto_ptr 已足以应付所

有相关的工作——实际上,auto_ptr 有一个重大的缺陷,就是它不能被用在STL 容器中——而是因为现在的C+

+标准在制定时并未能对智能指针进行全面的考察。按照C++标准委员会成员Herb Sutter 的说法,只有一种标

准的智能指针是一件可羞的事情:首先,智能指针所能做的许多有用的事情,是可怜的auto_ptr 不能完成

的;其次,在有些情况下使用auto_ptr 可能会造成问题,上面所说的不能在容器中使用就是一例。实际上,许

多程序员已经开发了各种有用的智能指针,有些甚至在auto_ptr 被定为标准之前就已存在,但问题是,它们不

是标准的。在这样的情况下,C++标准委员会考虑引入新的智能指针,也就是自然而然的事情了。目前进入委员会

视野的,主要有两种智能指针方案:Boost 智能指针和Loki 智能指针。前者是由C++标准委员会库工作组发起的

Boost 组织开发的,而后者由世界级的C++专家Andrei Alexandrescu 开发,并在他所著的“Modern C++

Design”一书中进行了详细的阐释。下面,让我们分别来看一看这两种方案各自的技术特点。

一、 Boost 智能指针

Boost 的智能指针方案实现了五种智能指针模板类,每种智能指针都用于不同的目的。这五种智能指针是:

template<typename T> class scoped_ptr;

template<typename T> class scoped_array;

template<typename T> class shared_ptr;

template<typename T> class shared_array;

template<typename T> class weak_ptr;

下面将分别介绍它们各自的特性,并给出相应的使用实例:

?? scoped_ptr:意在用作指向自动(栈)对象的、不可复制的智能指针。该模板类存储的是指向动态分配的

对象(通过new 分配)的指针。被指向的对象保证会被删除,或是在scoped_ptr 析构时,或是通过显式地调用

reset 方法。注意该模板没有共享所有权或是所有权转让语义。同时,它也是不可复制的(noncopyab

le)。正因为如此,在用于不应被复制的指针时,它比shared_ptr std:auto_ptr 要更安全。与auto_ptr

一样,scoped_ptr 也不能用于STL 容器中;要满足这样的需求,应该使用shared_ptr。另外,它也不能用于

存储指向动态分配的数组的指针,这样的情况应使用scoped_array

下面是使用scoped_ptr 的一个简单实例:

class CTest

{

public:

CTest() : m_id(0) {}

CTest(int id) : m_id(id) {}

~CTest() { std::cout << "id: " << m_id << " - Destructor is being called\n"; }

void SetId(int id) { m_id = id; }

int GetId() { return m_id; }

void DoSomething()

{ std::cout << "id: " << m_id << " - Doing something\n"; }

private:

int m_id;

};

void main()

{

boost::scoped_ptr<CTest> pTest(new CTest);

pTest->DoSomething();

}

其运行结果为:

id: 0 - Doing something

id: 0 - Destructor is being called

(以下的几个例子所用的CTest 类的定义完全相同,为节省篇幅,不再列出——作者)

显然,尽管我们自己没有调用deletepTest 仍然为我们正确地删除了它所指向的对象。看起来scoped_ptr

的用途和auto_ptr 十分类似,但实际上,scoped_ptr 类型的指针的所有权不可转让,这一点是和auto_ptr

相当不同的。

?? scoped_array:该模板类与scoped_ptr 类似,但意在用于数组而不是单个对象。std::vector 可用于

替换scoped_array,并且远为灵活,但其效率要低一点。在不使用动态分配时,boost::array 也可用于替换

scoped_array

下面是一个使用scoped_array 的实例:

void main()

{

boost::scoped_array<CTest> pTest(new CTest[2]);

pTest[0].SetId(0);

pTest[1].SetId(1);

pTest[0].DoSomething();

pTest[1].DoSomething();

std::cout << '\n';

}

其运行结果为:

id: 0 - Doing something

id: 1 - Doing something

id: 1 - Destructor is being called

id: 0 - Destructor is being called

scoped_array 将负责使用delete [],而不是delete 来删除它所指向的对象。

?? shared_ptr:意在用于对被指向对象的所有权进行共享。与scoped_ptr 一样,被指向对象也保证会被删

除,但不同的是,这将发生在最后一个指向它的shared_ptr 被销毁时,或是调用reset 方法时。shared_ptr

符合C++标准库的可复制构造CopyConstructible)和可赋值Assignable)要求,所以可被用于标

准的库容器中。另外它还提供了比较操作符,所以可与标准库的关联容器一起工作。shared_ptr 不能用于存储指

向动态分配的数组的指针,这样的情况应该使用shared_array。该模板的实现采用了引用计数技术,所以无法

正确处理循环引用的情况。可以使用weak_ptr 打破循环shared_ptr 还可在多线程环境中使用。

下面的例子演示怎样将shared_ptr 用于std::vector 中:

typedef boost::shared_ptr<CTest> TestPtr;

void PT(const TestPtr &t)

{

std::cout << "id: " << t->GetId() << "\t\t"

<< "use count: " << t.use_count() << '\n';

}

void main()

{

std::vector<TestPtr> TestVector;

TestPtr pTest0(new CTest(0));

TestVector.push_back(pTest0);

TestPtr pTest1(new CTest(1));

TestVector.push_back(pTest1);

TestPtr pTest2(new CTest(2));

TestVector.push_back(pTest2);

std::for_each(TestVector.begin(), TestVector.end(), PT);

std::cout << '\n';

pTest0.reset();

pTest1.reset();

pTest2.reset();

std::for_each(TestVector.begin(), TestVector.end(), PT);

std::cout << '\n';

TestVector.clear();

std::cout << '\n';

std::cout << "exiting...\n";

}

其运行结果为:

id: 0 use count: 2

id: 1 use count: 2

id: 2 use count: 2

id: 0 use count: 1

id: 1 use count: 1

id: 2 use count: 1

id: 0 - Destructor is being called

id: 1 - Destructor is being called

id: 2 - Destructor is being called

exiting...

运行结果中的“use count”是通过shared_ptr use_count()方法获得的使用计数,也就是,对所存储

指针进行共享的shared_ptr 对象的数目。我们可以看到,在通过new 分配了3 CTest 对象,并将相应的sh

ared_ptr 对象放入TestVector 后,三个使用计数都为2;而在我们使用reset()方法复位pTest0pTest

1 pTest2 后,TestVector 中的各个shared_ptr 对象的使用计数变成了1。这时,我们调用TestVector

clear()方法清除它所包含的shared_ptr 对象;因为已经没有shared_ptr 对象再指向我们先前分配的3

CTest 对象,这3 个对象也随之被删除,并导致相应的析构器被调用。

?? shared_array:该模板类与shared_ptr 类似,但意在用于数组而不是单个对象。指向std::vector

shared_ptr 可用于替换scoped_array,并且远为灵活,但其效率也要低一点。

下面是使用实例:

void main()

{

boost::shared_array<CTest> pTest1(new CTest[2]);

pTest1[0].SetId(0);

pTest1[1].SetId(1);

std::cout << "use count: " << pTest1.use_count() << "\n\n";

boost::shared_array<CTest> pTest2(pTest1);

std::cout << "use count: " << pTest1.use_count() << "\n\n";

pTest1.reset();

pTest2[0].DoSomething();

pTest2[1].DoSomething();

std::cout << '\n';

std::cout << "use count: " << pTest1.use_count() << "\n\n";

}

其运行结果为:

use count: 1

use count: 2

id: 0 - Doing something

id: 1 - Doing something

use count: 1

id: 1 - Destructor is being called

id: 0 - Destructor is being called

如此例所示,我们通过new 所分配的数组只有在指向它的pTest1 pTest2 都被销毁或复位后才被删除。

?? weak_ptr:该模板类存储已由shared_ptr 管理的对象弱引用。要访问weak_ptr 所指向的对

象,可以使用shared_ptr 构造器或make_shared 函数来将weak_ptr 转换为shared_ptr。指向被管理对象

的最后一个shared_ptr 被销毁时将删除该对象,即使仍有weak_ptr 指向它也是如此。与原始指针不同的是,

届时最后一个shared_ptr 会检查是否有weak_ptr 指向该对象,如果有的话就将这些weak_ptr 置为空。这样

就不会发生使用原始指针时可能出现的悬吊指针dangling pointer)情况,从而获得更高的安全水平。

weak_ptr 符合C++标准库的可复制构造CopyConstructible)和可赋值Assignable)要求,

所以可被用于标准的库容器中。另外它还提供了比较操作符,所以可与标准库的关联容器一起工作。

void main()

{

boost::shared_ptr<CTest> pTest(new CTest);

boost::weak_ptr<CTest> pTest2(pTest);

if(boost::shared_ptr<CTest> pTest3 = boost::make_shared(pTest2))

pTest3->DoSomething();

pTest.reset();

assert(pTest2.get() == NULL);

}

其运行结果为:

id: 0 - Doing something

id: 0 - Destructor is being called

main 函数最后的断言确认了pTest2 所存储的指针的确已被置为NULL

显然,Boost 的智能指针方案会让我们产生这样的疑问:如果我们还需要其他类型的智能指针(比如支持CO

M 的智能指针),是否意味着我们必须在C++中再增加智能指针类型,或是采用非标准的实现呢?在泛型技术已得

到极大发展的今天,Boost