一 . boost库中smart_ptr产生的原因,所发挥的作用
我们知道,为了更安全使用指针,不再让C/C++程序猿为了代码的内存管理而苦恼,VC和VS都带有智能指针auto_ptr供程序猿调用。如果还没了解建议先了解auto_ptr源代码,下面是之前对它的解析boke:
智能指针auto_ptr的解析
可无论是vc下的auto_ptr还是vs下的auto_ptr都存在设计上的缺陷,vs下的对指向空间所有权的转移是不符合人们一般习惯认识的,在智能指针对象赋值运算后,会意外让本来拥有所有权的对象失去所有权。vc下auto_ptr也会将所有权移交给对方,但自己仍有操作指向空间的权利,而没有了析构指向空间(或free)的权利,只有拥有所有权才有,但当所有权拥有着析构(free掉空间后),没有拥有权的指针再去操作,会发生错误,因为空间已经释放。
auto_ptr也不能用于数组指针,它所封装的是delete 不是delete[];
boost.smart_ ptr 库是对C++98 标准的一个绝佳补充。它提供了六种智能指针,包括
scoped.ptr、scoped array.shared_ptr、shared_ array,weak_ _ptr 和intrusive_ptr,
从各个方面来增强std: :auto_ ptr,而且是异常安全的。库中的两个类shared_ ptrweak.ptr 已被收入到C++新标准的TR1库中。
本篇我们先介绍介绍scoped_ptr、scoped_ array
它们都是很轻量级的对象,速度与原始指针相差无几,对于所指的类型T 也仅有一个很小且很合理的要求: 类型T 的析构函数不
能抛出异常。
这些智能指针都位于名字空间boost,为了使用smart_ ptr组件,需要包含头文件即:
boost/smart_ptr.hpp
boost/smart_ptr.hpp
using namespace boost;
二 . scoped_ptr
1)scoped_ptr介绍,类摘要代码分析
当然,scopy_ptr是一个和auto_ptr类似的智能指针,保证了动态创建的对象在任何时候可以被正确的释放删除,scoped_ptr顾名思义,这种智能指针只能在本作用域内使用,不可以转让。
直接从它的类摘要代码入手了解:
template
class scoped_ptr{
private:
T *px; //智能指针都是将原生指针封装进类中,具体的实现操作还得靠它
scoped_ptr(scoped_ptr const &); //特别重点的,将拷贝构造函数和赋值重载放进私有成员正是为了不被转让,只能让拥有着使用;
scoped_ptr & operator=(scoped_ptr const &);
public:
explicit scoped_ptr(T *p = 0);
~scoped_ptr(); //析构函数,实现当中肯定包含有对指针所指向空间的delete
void reset(T *p = 0);
T & operator*()const;
T * operator->()const;
T * get()const;
operator unspecified-bool-type()const;
void swap(scoped_ptr & b);
};
1.scoped.ptr的构造函数接受一个类型为T*的指针p,创建出一个scoped ptr 对象,并在内部保存指针参数p。
p 必须是一一个new 表达式动态分配的结果,或者是个空指针(0)。当scoped_ptr 对象的生命期结束时,
析构函数~scoped_ ptr ()会使用delete 操作符自动销毁所保存的指针对象,从而正确地回收资源。
2.scoped ptr 同时把拷贝构造函数和赋值操作符都声明为私有的,禁止对智能指针的复制操作,保证了被它
管理的指针不能被转让所有权。
3.成员函数reset () 的功能是重置scoped_ ptr: 它删除原来保存的指针,再保存新的指针值p。如果p 是空指针,
那么scoped ptr 将不持有任何指针。一般情况下reset () 不应该被调用,因为它违背了scoped.ptr 的本意一资源
应该一直由scoped ptr 自己自动管理。
4.scoped_ptr 用operator* ()和operator->() 重载了解引用操作符*和箭头操作符->,以模仿被代理的原始指针的行为,
因此可以把scoped ptr 对象如同指针一样使用。如果scoped.ptr 保存空指针,那么这两个操作的行为未定义。
scoped ptr 不支持比较操作,不能在两个scoped ptr 之间、scoped ptr 和原始指针或空指针之间进行相等或者不相
等测试,我们也无法为它编写额外的比较函数,因为它已经将operator==和operator!=两个操作符重载都声明为私有的。
但scoped ptr 提供了一个可以在bool 语境(context) 中自动转换成bool 值(如if 的条件表达式) 的功能,用来测试scoped_ ptr 是否持有一个有效的指针(非空)。它可以代替与空指针的比较操作,而且写法更简单。
2). 成员函数的解析及用法展示
成员函数讲解
explicit scoped_ptr(T* p=0)
构造函数,存储p的一份拷贝。注意,p 必须是用operator new分配的,或者是null. 在构造的时候,不要求T必须是一个完整的类型。当指针p是调用某个分配函数的结果而不是直接调用new得到的时候很有用:因为这个类型不必是完整的,只需要类型T的一个前向声明就可以了。这个构造函数不会抛出异常。
~scoped_ptr()
删除指针所指向的对象。类型T在被销毁时必须是一个完整的类型。如果scoped_ptr在它被析构时并没有保存资源,它就什么都不做。这个析构函数不会抛出异常。
void reset(T* p=0);
重置一个 scoped_ptr 就是删除它已保存的指针,如果它有的话,并重新保存p. 通常,资源的生存期管理应该完全由scoped_ptr自己处理,但是在极少数时候,资源需要在scoped_ptr的析构之前释放,或者scoped_ptr要处理它原有资源之外的另外一个资源。这时,就可以用reset,但一定要尽量少用它。(过多地使用它通常表示有设计方面的问题) 这个函数不会抛出异常。
T& operator*() const;
该运算符返回一个智能指针中存储的指针所指向的对象的引用。由于不允许空的引用,所以解引用一个拥有空指针的scoped_ptr将导致未定义行为。如果不能肯定所含指针是否有效,就用函数get替代解引用。这个函数不会抛出异常。
T* operator->() const;
返回智能指针所保存的指针。如果保存的指针为空,则调用这个函数会导致未定义行为。如果不能肯定指针是否空的,最好使用函数get。这个函数不会抛出异常。
T* get() const;
返回保存的指针。应该小心地使用get,因为它可以直接操作裸指针。但是,get使得你可以测试保存的指针是否为空。这个函数不会抛出异常。get通常在调用那些需要裸指针的函数时使用。
operator unspecified_bool_type() const
返回scoped_ptr是否为非空。返回值的类型是未指明的,但这个类型可被用于Boolean的上下文(boolean context)中。在if语句中最好使用这个类型转换函数,而不要用get去测试scoped_ptr的有效性。
void swap(scoped_ptr& b)
交换两个scoped_ptr的内容。这个函数不会抛出异常;
scoped_ptr的用法和auto_ptr几乎一样,大多数情况下它都可以与auto_ptr互相替换,它也可以从一个auto_ptr获得指针的管理权(同时auto_ptr失去管理权)。
scoped_ptr和auto_ptr一样不能用作容器的元素,但原因不同:auto_ptr是因为它的转移语义(请看上篇博客),而scoped_ptr则是因为不支持拷贝和赋值,不符合容器对元素类型的要求。
scoped_ptr和auto_ptr的根本区别在于所有权。auto_ptr特意被设计为指针的所有权是可以被转移的,可以在函数之间传递,同一时刻只能有一个auto_ptr管理指针。而scoped_ptr把拷贝构造函数和赋值函数都声明为私有的,拒绝了指针所有权的转让,只有scoped_ptr自己能够管理指针,其他任何人都无权访问被管理的指针,从而保证了指针的绝对安全。
如下代码所示:
auto_ptr<int> ap(new int(10)); //一个int自动指针
scoped_ptr<int>sp (ap); //从auto_ptr获得原始指针
assert(ap.get() == 0); //原auto_ptr不再拥有指针
ap.rest(new int(20)); //auto_ptr拥有新的指针
cout <<*ap<<","<<*sp<auto_ptr<int>ap2;
ap2 = ap; //ap2从ap获得原始指针,所有权转移
assert(ap.get() == 0); //ap不再拥有原始指针
scoped_ptr<int>sp2; //另一个scoped_ptr
sp2 = sp; //赋值操作,无法通过编译
如果代码企图从一个scoped_ptr构造或赋值另一个scoped_ptr(最后一行),那么编译器会报错,阻止这么做,保护了你的代码。 比起auto_ptr,scoped_ptr更明确地表明了代码原始编写者的意图;只能在定义的作用域内使用,不可转让,更明确所以在特定的场景下很适合用;
三 . scoped_array
scoped_ array 很像scoped_ ptr,它包装了new[ ]操作符(不是单纯的new) 在堆上分
配的动态数组,为动态数组提供了一个代理,保证可以正确地释放内存。
scoped_ array 弥补了标准库中没有指向数组的智能指针的缺憾。
1)scoped_array类摘要源码的解析
template<class T> class shared_array
{
private:
// Borland 5.5.1 specific workarounds
typedef checked_array_deleter deleter;
typedef shared_array this_type;
public:
typedef T element_type;
explicit shared_array(T * p = 0);
template<class D> shared_array(T * p, D d);
void reset(T * p = 0);
template <class D> void reset(T * p, D d);
T & operator[] (std::ptrdiff_t i) const;
T * get() const;
bool unique() const;// never throws
long use_count() const; // never throws
void swap(shared_array & other);// never throws
operator unspecified-bool-type() const;
private:
T * px; // contained pointer
detail::shared_count pn; // reference counter
};
主要特点如下:
1,构造函数接受的指针p必须是new[]的结果,而不是new表达式的结果;
2,没有*、->操作符重载,scoped_array持有的不是一个普通指针;
3,析构函数使用delete[],而不是delete;
4,提供operator[]重载,可以像普通数组一样使用下标访问元素;
5,没有begin(),end()等类似容器迭代器操作函数。
scoped array 与scoped ptr 源于相同的设计思想,故而用法非常相似: 它只能在被声
明的作用域内使用,不能拷贝、赋值。唯一不同的是scoped._array 包装的是new[ ]产生的指针,并在析构时调用delete [],因为它管理的是动态数组,而不是单个动态象。
2)用法实例:
#include
#include
#include
using namespace boost;
using namespace std;
int main(){
int *arr = new int[100];//a dynamically allocated array
scoped_array<int> sa(arr); // scoped_array object proxied original allocated array 代理原始动态数组
fill_n(&sa[0],100,5);//use stdandard library algorithm,fill the entire array with 5
sa[30] = sa[10] + sa[20];//support operator[]
cout << sa[30] << endl;
//desturctor,automatically destory resource
}
//运行结果是10
同学们可以去看Boost程序库完全开发指南了解更多Boost库的知识,本篇博客对scoped_ptr和scoped_ptr这对兄弟做了解析,这只是Boost库提供指针的两个,还有特别重要的shared_ptr,运用十分广泛,之后会进行解析。