class Example : public boost::enable_shared_from_this < Example >{
public:
int x;
Example() :x(233){}
Example(int _x) :x(_x){}
};
int main(){
for (;;){
boost::shared_ptr<Example> sp = boost::make_shared<Example>(666);
assert(sp->x == 666);
boost::shared_ptr<Example> p = sp->shared_from_this();
assert(p.use_count() == 2);
assert(p->x == 666);
}
}
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
常见的shared_ptr的使用方式:
std::shared_ptr<int> p(new int); // or '=shared_ptr<int>(new int)' if you insist
auto p = std::make_shared<int>(); // or 'std::shared_ptr<int> p' if you insist
两者的区别和联系:
第一种要分配内存两次,一次给管理的对象,另一个是给引用计数,
make_shared则是只有一次内存。
基本的使用方式
int* p = new int(233);
boost::shared_ptr<int> sp(p);
//然后就不能再delete p了, 否则重复释放
boost::shared_ptr<int> sp;
assert(!sp);
boost::shared_ptr<int> sp;
assert(!sp);
int* p = new int(233);
sp.reset(p);
assert(sp);
assert(233 == *sp);
防止可能的泄露
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() );
}
防止循环引用
class Humen{};
class Male:public Humen{
boost::shared_ptr<Humen> _wife;
};
class Female:public Humen{
boost::shared_ptr<Humen> _husband;
};
class SingleDog:public Humen{
};
class Request {
std::shared_ptr<void> pData;
public:
//Called by clients to set user-data
void setUserData(const std::shared_ptr<void>& p) {
pData = p;
}
};
class Response {
std::shared_ptr<void> pData; // Copied from Request
public:
//Called by clients
template<typename T>
auto getUserData() {
return std::static_pointer_cast<T>(pData);
}
};
//Create a request and attach user data
Request r;
r.setUserData(std::make_shared<Context>(/*..*/)); // Set a context
// Send request asynchrounously.
// Response handler callback
void onResponse(Response* pr) {
auto pc = pr->getUserData<Context>();
// Process response using context....
// No explicit context delete here
}
enable_shared_from_this:
这样使用是不对的,sp1和sp2两者互相不知道对方,所以当离开作用域的时候,会释放两遍。 同样的道理:如果一个成员函数返回this的shared_ptr,也会出现上面的问题。成员函数dangerous是不会了解外面的sp1已经拥有了shared_ptr的资源。
正确的编写dangerous的姿势是如下:
但是要注意的是not_dangerous正确的调用方式是,shared_ptr包裹新创建的对象后创建
常见的shared_ptr的使用方式:
两者的区别和联系:
基本的使用方式
防止可能的泄露
防止循环引用
但我真需要一个mate怎么办? boost还有weak_ptr.
wear_ptr是一个弱化的shared_ptr, 它的存在不会阻碍析构, 而且可以从一个有效的wear_ptr中获取对应的shared_ptr。weak_ptr相当于一个旁观者, 它并不提供完整的指针操作, 如果你要使用其管理的对象, 必须取其shared_ptr.
将以上代码换成如下便可破解循环引用:
boost::weak_ptr
上面已经提到了weak_ptr, 可以看到, weak_ptr是为了配合shared_ptr而引入的一种智能指针, 它更像shared_ptr的一个助手而不是智能指针。
Linux多线程服务端编程例子
假设有 Stock 类,代表一只股票的价格。每一只股票有一个唯一的字符串标识,比如 Google 的 key 是 "NASDAQ:GOOG",IBM 是 "NYSE:IBM"。Stock 对象是个主动对象,它能不断获取新价格。为了节省系统资源,同一个程序里边每一只出现的股票只有一个 Stock 对象,如果多处用到同一只股票,那么 Stock 对象应该被共享。如果某一只股票没有再在任何地方用到,其对应的 Stock 对象应该析构,以释放资源,这隐含了“引用计数”。
get() 的逻辑很简单,如果在 stocks 里找到了 key,就返回 stocks[key];否则新建一个 Stock,并存入 stocks_[key]。细心的读者或许已经发现这里有一个问题,Stock 对象永远不会被销毁,因为map 里存的是 shared_ptr,始终有“铁丝”绑着。
这么做固然 Stock 对象是销毁了,但是程序却出现了轻微的内存泄漏,为什么? 因为 stocks 的大小只增不减,stocks.size() 是曾经存活过的 Stock 对象的总数,即便活的 Stock 对象数目降为 0。或许有人认为这不算泄漏,因为内存并不是彻底遗失不能访问了,而是被某个标准库容器占用了。我认为这也算内存泄漏,毕竟 是“战场”没有打扫干净。
其实,考虑到世界上的股票数目是有限的,这个内存不会一直泄漏下去,大不了把每只股票的对象都创建一遍,估计泄漏的内存也只有几兆字节。如果这是一个其他类型的对象池,对象的 key 的集合不是封闭的,内存就会一直泄漏下去。
解决的办法是,利用 shared_ptr 的定制析构功能。shared_ptr 的构造函数可以有一个额外的模板类型参数,传入一个函数指针或仿函数 d,在析构对象时执行d(ptr),其中 ptr 是 shared_ptr 保存的对象指针。shared_ptr 这么设计并不是多余的,因为反正要在创建对象时捕获释放动作,始终需要一个 bridge。
警惕的读者可能已经发现问题,那就是我们把一个原始的 StockFactory this 指针保存在了 boost::function 里(*** 处),这会有线程安全问题。如果这个 Stock-Factory 先于 Stock 对象析构,那么会 core dump。
StockFactory::get() 把原始指针 this 保存到了 boost::function 中(*** 处),如果 StockFactory 的 生 命 期 比 Stock 短, 那 么 Stock 析 构 时 去 回 调 StockFactory::deleteStock 就会 core dump。似乎我们应该祭出惯用的 shared_ptr 大法来解决对象生命期问题,但是 StockFactory::get() 本身是个成员函数,如何获得一个指向当前对象的 shared_ptr 对象呢?
为了使用 shared_from_this(),StockFactory 不能是 stack object,必须是 heap object 且由 shared_ptr 管理其生命期,即:
万事俱备,可以让 this 摇身一变,化为 shared_ptr 了。
这样一来,boost::function 里保存了一份 shared_ptr,可以保证调用 StockFactory::deleteStock 的时候那个 StockFactory 对象还活着。 注意一点,shared_from_this() 不能在构造函数里调用,因为在构造 StockFactory 的时候,它还没有被交给 shared_ptr 接管。
最后一个问题,StockFactory 的生命期似乎被意外延长了。
把 shared_ptr 绑(boost::bind)到 boost:function 里,那么回调的时候 StockFactory 对象始终存在,是安全的。这同时也延长了对象的生命期,使之不短于绑得的 boost:function 对象。
有时候我们需要“如果对象还活着,就调用它的成员函数,否则忽略之”的语意,就像 Observable::notifyObservers() 那样,我称之为“弱回调”。这也是可以实现的,利用 weak_ptr,我们可以把 weak_ptr 绑到 boost::function 里,这样对象的生命期就不会被延长。然后在回调的时候先尝试提升为 shared_ptr,如果提升成功,说明接受回调的对象还健在,那么就执行回调;如果提升失败,就不必劳神了。
shared_ptr的线程安全
请注意,以上是 shared_ptr 对象本身的线程安全级别,不是它管理的对象的线程安全级别。 良好的使用shared_ptr的方式
写也要加锁:
shared_ptr on void