Boost的智能指针

  Boost介绍

Boost是“一个免费的,可移植的,同步评测的C++库,Boost堪称是新类库的典范,特别是其中那些能够与ISO C++标准库良好的协同工作的库。”但是Boost不仅仅是一个库的集合。它也是一个快速发展的开发者社区,这些开发者创建,使用以及参与讨论Boost。这个库堪称是一个设计稳固类的精典范例。Boost库的质量和它的技术标准是十分令人惊异的。Boost可移植性标准确保了当我们将自己的代码从一个平台上移动到另一个平台上时,我们的库仍然会正常工作。
 
Boost目前支持35个库,这些库都是可以免费使用的,它们当中的很多内容都已经被用于商业应用软件的开发。
 
Boost的智能指针(smart pointer)库smart_ptr。smart_ptr是一个能够反映出Boost的创新以及完美设计的好例子。
 
智能指针
智能指针是存储指向动态分配(堆)对象指针的类。除了能够在适当的时间自动删除指向的对象外,他们的工作机制很像C++的内置指针。智能指针在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象。他们也可以用于跟踪被多用户共享的动态分配对象。
 
事实上,智能指针能够做的还有很多事情,例如处理线程安全,提供写时复制,确保协议,并且提供远程交互服务。有能够为这些ESP (Extremely Smart Pointers)创建一般智能指针的方法,但是并没有涵盖近来。
 
智能指针的大部分使用是用于生存期控制,阶段控制。它们使用operator->和operator*来生成原始指针,这样智能指针看上去就像一个普通指针。
 
这样的一个类来自标准库:std::auto_ptr。它是为解决资源所有权问题设计的,但是缺少对引用数和数组的支持。并且,std::auto_ptr在被复制的时候会传输所有权。在大多数情况下,需要更多的和/或者是不同的功能。这时就需要加入smart_ptr类。
 
smart_ptr 类
在Boost中的智能指针有:
。scoped_ptr,用于处理单个对象的唯一所有权;与std::auto_ptr不同的是,scoped_ptr可以被复制。
。scoped_array,与scoped_ptr类似,但是用来处理数组的
。shared_ptr,允许共享对象所有权
。shared_array,允许共享数组所有权
 
scoped_ptr 智能指针与std::auto_ptr不同,因为它是不传递所有权的。事实上它明确禁止任何想要这样做的企图!这在需要确保指针任何时候只有一个拥有者时的任何一种情境下都是非常重要的。如果不去使用scoped_ptr,我们可能倾向于使用std::auto_ptr,先看看下面的代码:
auto_ptr MyOwnString   (new string("This is mine to keep!"));
auto_ptr NoItsMine(MyOwnString?);
cout << *MyOwnString << endl;    // Boom
这段代码显然将不能编译通过,因为字符串的所有权被传给了NoItsMine。这不是std::auto_ptr的设计缺陷—而是一个特性。尽管如此,当需要MyOwnString达到上面的代码预期的工作效果的话,可以使用scoped_ptr:
scoped_ptr MyOwnString?
(new string("This is mine to keep for real!"));
// Compiler error - there is no copy constructor.
scoped_ptr TryingToTakeItAnyway(MyOwnString);
scoped_ptr通过从boost::noncopyable继承来完成这个行为(可以查看Boost.utility库)。不可复制类声明复制构造函数并将赋值操作符声明为private类型。
 
scoped_array 与scoped_ptr显然是意义等价的,但是是用来处理数组的。在这一点标准库并没有考虑—除非使用std::vector,在大多数情况下这样做是可以的。
用法和scoped_ptr类似:
typedef〈tuples::tupleint> ArrayTuple;
scoped_array MyArray(new ArrayTuple[10]);
tuples::get<0>(MyArray[5]) ="The library Tuples is also part of Boost";
tuple是元素的集合—例如两倍,三倍,和四倍。Tuple的典型用法是从函数返回多个值。Boost Tuple库可以被认为是标准库两倍的扩展,目前它与近10个tuple元素一起工作。支持tuple流,比较,赋值,卸包等等。
当scoped_array越界的时候,delete[]将被正确的调用。这就避免了一个常见错误,即是调用错误的操作符delete。
 
shared_ptr —引用数智能指针。大部分人都应当有过使用智能指针的经历。Boost shared_ptr实现使用一个从堆中分配来的引用计数器。
shared_ptr完成了我们所希望的工作:它负责在不使用实例时删除由它指向的对象(pointee),并且它可以自由的共享它指向的对象(pointee)。
void PrintIfString(const any& Any) {
if (const shared_ptr* s =<any_cast >(&Any)) {
cout << **s << endl;
}
}
int main(int argc, char* argv[])
{
std::vector Stuff;
shared_ptr SharedString1(new string
("Share me. By the way,Boost.any is another useful Boost library"));
shared_ptr SharedString2(SharedString1);
shared_ptr SharedInt1(new int(42));
shared_ptr SharedInt2(SharedInt1);
Stuff.push_back(SharedString1);
Stuff.push_back(SharedString2);
Stuff.push_back(SharedInt1);
Stuff.push_back(SharedInt2);
// Print the strings
for_each(Stuff.begin(), Stuff.end(),PrintIfString);
Stuff.clear();
// The pointees of the shared_ptr's
// will be released on leaving scope
// shared_ptr的pointee离开这个范围后将被释放
return 0;
}
 
any库提供了存储所有东西的方法。在包含类型中需要的是它们是可拷贝构造的(CopyConstructible),析构函数这里绝对不能引发,它们应当是可赋值的。我们如何存储和传递“所有事物”?无区别类型(读作void*)可以涉及到所有的事物,但这将意味着将类型安全抛之脑后。any库提供类型安全。所有满足any需求的类型都能够被赋值,但是解开的时候需要知道解开类型。any_cast是解开由any保存着的值的钥匙,any_cast与dynamic_cast的工作机制是类似的—指针类型的类型转换通过返回一个空指针成功或者失败,因此赋值类型的类型转换抛出一个异常(bad_any_cast)而失败。
 
shared_array 与shared_ptr作用是相同的,只是它是用于处理数组的。
shared_array MyStrings( new Base[20] );
 
深入shared_ptr实现
创建一个简单的智能指针是非常容易的。但是创建一个能够在大多数编译器下通过的智能指针就有些难度了。而创建同时又考虑异常安全就更为困难了。Boost::shared_ptr这些全都做到了,下面便是它如何做到这一切的。
首先,类的定义:很显然,智能指针是(几乎总是)模板。
template class shared_ptr {
//公共接口是:
explicit shared_ptr(T* p =0):px(p) {
// fix: prevent leak if new throws
try { pn = new long(1); }
catch (...) { checked_delete(p); throw; }
}
/*构造函数是explicit的,就像大多数的构造函数一样可以带有一个参数。另外一个值得注意的是引用数的堆分配是由一个try-catch块保护的。如果没有这个,我们得到的将是一个有缺陷的智能指针,如果引用数没有能够成功分配,它将不能正常完成它自己的工作。*/
 
~shared_ptr() { dispose(); }
/*析构函数执行另一个重要任务:若引用数下降到零,它应当能够安全的删除指向的对象(pointee)。析构函数将这个重要任务委托给了另外一个方法:dispose。*/
void dispose() {
if (—*pn == 0)
{ checked_delete(px); delete pn; } }
/*正如所看到的,引用数(pn)在减少。如果它减少到零,checked_delete在所指对象 (px)上被调用,而后引用数(pn)也被删除了。checked_delete函数确保指针代表的是一个完整的类型。
这是第一个赋值运算符:*/
template shared_ptr& operator=
(const shared_ptr& r) {
share(r.px,r.pn);
return *this;
}
/*这是成员模版,如果不是这样,有两种情况:
1. 如果没有参数化复制构造函数,类型赋值Base = Derived无效。
2. 如果有参数化复制构造函数,类型赋值将生效,但同时创建了一个不必要的临时smart_ptr。
赋值运算符的实际工作是由share函数完成的: */
 
void share(T* rpx, long* rpn) {
if (pn = rpn) {//why not px = rpx? fails when both == 0
++*rpn;
// done before dispose() in case rpn transitively dependent on *this
dispose();
px = rpx;
pn = rpn;
}
}
 
/*需要注意的是自我赋值(更准确地说是自我共享)是通过比较引用数完成的,而不是通过指针。为什么这样呢?因为它们两者都可以是零,但不一定是一样的。*/
template shared_ptr(const shared_ptr& r):px(r.px) { // never throws
++*(pn = r.pn);
}
/*此版本是一个模版化的拷贝构造函数。赋值运算符以及赋值构造函数在这里同样也有一个非模版化的版本:*/
shared_ptr(const shared_ptr& r):
// never throws
px(r.px) { ++*(pn = r.pn);}
shared_ptr& operator=(const shared_ptr& r){
share(r.px,r.pn);
return *this;
}
 
/*reset函数就像其名字那样,重新设置所指对象(pointee)。在将要离开作用域的时候,若需要销毁所指对象(pointee)它将非常方便的帮你完成,或者简单的使缓存中的值失效。*/
void reset(T* p=0) {// fix: self-assignment safe
if ( px == p ) return;
if (—*pn == 0)
{ checked_delete(px); }
else { // allocate new reference
// counter
// fix: prevent leak if new throws
try { pn = new long; }
catch (...) {
// undo effect of —*pn above to
// meet effects guarantee
++*pn;
checked_delete(p);
throw;
} // catch
} // allocate new reference counter
*pn = 1;
px = p;
} // reset
/*这里仍然请注意避免潜在的内存泄漏问题和保持异常安全的处理手段。
这样就有了使得智能指针发挥其“智能”的运算符:*/
// never throws
T& operator*() const { return *px; }
// never throws
T* operator->() const { return px; }
// never throws
T* get() const { return px; }
/*这仅仅是一个注释:有的智能指针实现从类型转换运算符到T*的转换。这不是一个好主意,这样做常会使你因此受到伤害。虽然get在这里看上去很不舒服,但它阻止了编译器的问题。*/
long use_count() const
{ return *pn; } // never throws
bool unique() const
{ return *pn == 1; } // never throws
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值