我对 C++11 中新内存
<memory>
标头的理解有点周,但据我所知,shared_ptr 是引用计数的 ptr,这使得复制它非常非常昂贵(特别是在 ARM arch 上)。 unique_ptr 几乎是 new/delete 的非常非常轻的包装器。而且它是可移动的,因此不会受到您创建它的范围的限制。我对这样的答案不感兴趣:让你的单线程代码为未来的多线程做好准备。假设代码是单线程的。
您对线程的关注有点转移注意力;两者之间有明显的对比,并且与线程几乎没有关系。如果您在单线程环境中使用这些类,您也许可以关闭原子操作支持;例如,使用 Boost,定义宏
BOOST_SP_DISABLE_THREADS
。
当您不太确定某个对象的生命周期,并且希望“房间里最后一个关灯的人”时,您可以使用
shared_ptr<>
- 即,您不希望该对象成为删除直到没有客户端再使用它。当您确切知道谁将删除该对象时,即当所指向对象的生命周期由范围精确界定时,您可以使用 unique_ptr<>
。
确实,复制
shared_ptr<>
不是免费的,但它离“非常非常昂贵”还很远。您需要为引用计数开销付出一些代价,但这就是使用它的全部意义:您需要跟踪对象的客户端;虽然涉及一点成本,但您可以获得不泄漏对象的好处。
在多线程构建中,基本上总是为
shared_ptr<>
所做的原子引用计数器增量/减量付费,即使对象从未在线程之间共享。
另一个缺点是
shared_ptr<>
的大小是普通指针大小的两倍。
由于这两个原因,
shared_ptr<>
对于性能关键型应用来说从来都不是一个好的选择。
多线程应用程序有几种类型的对象在线程之间共享,而大多数对象则不在线程之间共享。仅线程共享对象需要使用原子引用计数器递增/递减,而为大多数其他对象支付原子操作成本是愚蠢的。因此,为线程共享和线程非共享对象设置不同的(基本)类型并使用
boost::intrusive_ptr<>
来管理它们是很有意义的。线程共享对象有一个原子引用计数器,而线程非共享对象则有一个普通整数计数器。例如:
#include <atomic>
#include <boost/intrusive_ptr.hpp>
template<class Derived, class Counter>
class RefCounter
{
Counter ref_count_;
friend void intrusive_ptr_add_ref(RefCounter* p) {
++p->ref_count_;
}
friend void intrusive_ptr_release(RefCounter* p) {
if(!--p->ref_count_)
delete static_cast<Derived*>(p);
}
protected:
RefCounter() : ref_count_() {}
};
class NonThreadShared
: public RefCounter<NonThreadShared, unsigned>
{};
class ThreadShared
: public RefCounter<ThreadShared, std::atomic<unsigned> >
{};
int main() {
boost::intrusive_ptr<NonThreadShared> p(new NonThreadShared);
boost::intrusive_ptr<ThreadShared> q(new ThreadShared);
}