介绍
本文主要就是一些对boost::shared_ptr的一些翻译和个人见解,主要来源还是boost官网。
share_ptr模板类储存一个动态的分配对象,一般使用c++的new表达式。这个对象的指针会在最后一次shared_ptr析构的时候或者是reset的时候删除掉。一般的用法如下:
shared_ptr<X> p1( new X );
shared_ptr<void> p2( new int(5) );
shared_ptr会删除在它构造的时候所引用的指针包括他原始的类型,无论模板参数是什么。在上面的第2个例子中,当p2析构或者reset的时候,它会删除通过构造函数生成的原始的类型的int*指针,尽管它自己的类型保存的是一个void*。
每一个shared_ptr都有一个拷贝构造,move构造,赋值或move赋值对于c++的标准库,而且可以用于c++标准库的容器。
因为使用了引用计数,所以在shared_ptr的生存周期内是不会被回收的。比如,如果main()里面有一个share_ptr指向A,直接或 者间接要回shared_ptr的指针A,A的引用计数将会是2。析构原来的shared_ptr将会使A的引用计数为1。可以使用weak_ptr破坏循环引用。循环引用是一个很难被发现,同时也是一个真实存在的问题,所以,各位在开发当中也是需要注意此点。
它的模板类型是T,对象指针的类型。shared_ptr的大部分成员函数都不依赖于T;它也允许指向不完整的类型或者是void。成员函数(构造或者重置)都可以明确的设置。
shared_ptr<T>能明确的转换成shared_ptr<U>,当T*可以明确的转换成U*时。一般 的,shared_ptr<T>可以准确的转换成shared_ptr<U>,转换成shared_ptr<U> 当U是T的可访问的基类时或者转换成shared_ptr<void>。
现在shared_ptr也是c++11的标准,如同std::shared_ptr。
从boost 1.53开始,shared_ptr就可以被动态数组使用。完全可以使用数组类型(T[] 或 T[N])作为模板参数。这基本和使用不确定的数组T[]没有任何区别,或者一个固定的数组T[N];然后可以使用[]操作符去核实索引的范围。比如:
shared_ptr<double[1024]> p1( new double[1024] );
shared_ptr<double[]> p2( new double[n] );
最好的实践
一个简单的减少内存泄漏的指南:经常使用智能指针去持有new的结果。每一个有new的地方都可能有这样的代码:
shared_ptr<T> p(new Y);
上面,是一个能接受使用shared_ptr的地方;T和Y有相同的类型,或者通过参数Y来构造也是同样的。
如果看到遵守这个指南,那就自然的表明你没有一个明确的删除状态;try/catch结构也是很少的了。
避免使用临时的shared_ptr,去看看为什么这样会是危险的,考虑这样的例子:
void f(shared_ptr<int>, int);
int g();
void ok()
{
shared_ptr<int> p( new int(2) );
f( p, g() );
}
void bad()
{
f( shared_ptr<int>( new int(2) ), g() );
}
ok函数的方案是比较好的,反之bad函数以一个临时的shared_ptr来代替,可能导致内存泄漏。因为函数参数是未指定顺序的,可能new int(2)先,g()后,如果g函数抛出一个异常,我们无法得到shared_ptr的构造。可以看看Herb Sutter’s treatment这里关于这个问题的更多信息。
上面谈到的关于异常安全的问题可以被定义于boost/make_shared.hpp中的make_shared或allocate_shared工厂函数消息。这些工厂函数也可以提高效率对于内存分配。
个人见解
其实boost文档已经讲解的很详细,此处主要讲解shared_ptr的void模板参数和其它类型之间的转换。
编程的时候经常会用到void*来当做参数,比如函数的参数,或者是队列的消息投递时当成结构体来使用,如下:
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
void print(void *p)
{
int *pt = static_cast<int*>(p);
std::cout << *pt << std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
int *p = new int(4);
print(p);
delete p;
return 0;
}
当然,这是一个很 简单的例子,只是说明场景,这需要你自己去删除指针,而下面的例子则智能指针会帮你删除,不需要你负责:
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
typedef boost::shared_ptr<int> intPtr;
typedef boost::shared_ptr<void> voidPtr;
void print(voidPtr p)
{
intPtr p1 = boost::static_pointer_cast<int>(p);
if (!p1)
{
std::cout << "error" << std::endl;
return;
}
std::cout << *p1 << std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
intPtr p(new int(4));
print(p);
intPtr p1;
print(p1);
return 0;
}
以上代码主要是讲解如何把shared_ptr<void>转化成指定类型的shared_ptr,在实际应用中只要明白原理,想必复杂也不会难到大家的。
还有一个就是在基类和派生类之间的转化:
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
class animal
{
public:
virtual void eat() = 0;
virtual void shout() = 0;
};
typedef boost::shared_ptr<animal> animalPtr;
class dog : public animal
{
public:
virtual void eat()
{
std::cout << "bone" << std::endl;
}
virtual void shout()
{
std::cout << "wang wang wang" << std::endl;
}
};
typedef boost::shared_ptr<dog> dogPtr;
class cat : public animal
{
public:
virtual void eat()
{
std::cout << "fish" << std::endl;
}
virtual void shout()
{
std::cout << "miao miao miao" << std::endl;
}
};
typedef boost::shared_ptr<cat> catPtr;
void eat(const animalPtr& ptr)
{
ptr->eat();
}
void shout(const animalPtr& ptr)
{
ptr->shout();
}
int _tmain(int argc, _TCHAR* argv[])
{
dogPtr pDog(new dog);
eat(pDog);
shout(pDog);
catPtr pCat(boost::make_shared<cat>());
eat(pCat);
shout(pCat);
/// 以下演示如何把派生类转化为子类,不过一般以下转化比较少见
animalPtr pAnimal(new dog);
pDog = boost::dynamic_pointer_cast<dog>(pAnimal);
if (!pDog)
{
std::cout << "failed" << std::endl;
return 1;
}
std::cout << "success" << std::endl;
return 0;
}
以上算是以代码的方式诠释了boost文档当中的一些说明和用法。并适当增加了一些个人的见解。