同时 , shared_ptr 也支持移动 。从语义上来看 , 移动指的是所有权的传递 。如下:
auto w = std::make_shared<Widget>();auto w2 = std::move(w); // 此时w等于nullptr , w2.use_count()等于1我们将 w 对象 move 给 w2 , 意味着 w 放弃了对内存的所有权和管理 , 此时 w 对象等于 nullptr 。而 w2 获得了对象所有权 , 但因为此时 w 已不再持有对象 , 因此 w2 的引用计数为 1 。
文章插图
【C++如何正确使用智能指针?看完这4个点你就明白了】C/C++
性能
- 内存占用高 。 shared_ptr 的内存占用是裸指针的两倍 。因为除了要管理一个裸指针外 , 还要维护一个引用计数 。因此相比于 unique_ptr, shared_ptr 的内存占用更高
- 原子操作性能低 。 考虑到线程安全问题 , 引用计数的增减必须是原子操作 。而原子操作一般情况下都比非原子操作慢 。
- 使用移动优化性能 。shared_ptr 在性能上固然是低于 unique_ptr 。而通常情况 , 我们也可以尽量避免 shared_ptr 复制 。如果 , 一个 shared_ptr 需要将所有权共享给另外一个新的 shared_ptr , 而我们确定在之后的代码中都不再使用这个 shared_ptr , 那么这是一个非常鲜明的移动语义 。对于此种场景 , 我们尽量使用 std::move , 将 shared_ptr 转移给新的对象 。因为移动不用增加引用计数 , 因此性能比复制更好 。
- 1. shared_ptr 通常使用在共享权不明的场景 。有可能多个对象同时管理同一个内存时 。
- 2. 对象的延迟销毁 。陈硕在《linux 多线程服务器端编程》中提到 , 当一个对象的析构非常耗时 , 甚至影响到了关键线程的速度 。可以使用BlockingQueue<std::shared_ptr<void>>将对象转移到另外一个线程中释放 , 从而解放关键线程 。
我们往往会需要在类内部使用自身的 shared_ptr , 例如:
class Widget{public: void do_something(A& a) { a.widget = 该对象的shared_ptr; }}我们需要把当前 shared_ptr 对象同时交由对象 a 进行管理 。意味着 , 当前对象的生命周期的结束不能早于对象 a 。因为对象 a 在析构之前还是有可能会使用到a.widget 。
如果我们直接a.widget = this; , 那肯定不行 , 因为这样并没有增加当前 shared_ptr 的引用计数 。shared_ptr 还是有可能早于对象 a 释放 。
如果我们使用a.widget = std::make_shared<Widget>(this); , 肯定也不行 , 因为这个新创建的 shared_ptr , 跟当前对象的 shared_ptr 毫无关系 。当前对象的 shared_ptr 生命周期结束后 , 依然会释放掉当前内存 , 那么之后a.widget依然是不合法的 。
对于这种 , 需要在对象内部获取该对象自身的 shared_ptr, 那么该类必须继承std::enable_shared_from_this<T> 。代码如下:
class Widget : public std::enable_shared_from_this<Widget>{public: void do_something(A& a) { a.widget = shared_from_this(); }}这样才是合法的做法 。
weak_ptrweak_ptr 是为了解决 shared_ptr 双向引用的问题 。即:
class B;struct A{ shared_ptr<B> b;};struct B{ shared_ptr<A> a;};auto pa = make_shared<A>();auto pb = make_shared<B>();pa->b = pb;pb->a = pa;pa 和 pb 存在着循环引用 , 根据 shared_ptr 引用计数的原理 , pa 和 pb 都无法被正常的释放 。对于这种情况, 我们可以使用 weak_ptr:
class B;struct A{ shared_ptr<B> b;};struct B{ weak_ptr<A> a;};auto pa = make_shared<A>();auto pb = make_shared<B>();pa->b = pb;pb->a = pa;weak_ptr 不会增加引用计数 , 因此可以打破 shared_ptr 的循环引用 。通常做法是 parent 类持有 child 的 shared_ptr, child 持有指向 parent 的 weak_ptr 。这样也更符合语义 。
如何指针作为函数传参很多时候 , 函数的参数是个指针 。这个时候就会面临选择困难症 , 这个参数应该怎么传 , 应该是 shared_ptr , 还是 const shared_ptr& , 还是直接 raw pointer 更合适 。
1. 只在函数使用指针 , 但并不保存 。假如我们只需要在函数中 , 用这个对象处理一些事情 , 但不打算涉及其生命周期的管理 , 不打算通过函数传参延长 shared_ptr 的生命周期 。对于这种情况 , 可以使用 raw pointer 或者 const shared_ptr& 。即:
推荐阅读
- 芽孢茶正确泡法,最全黑茶的泡法专业泡法教给您
- 阿里巴巴怎么开店注册流程 如何阿里巴巴开店步骤
- 公司注销了淘宝怎么办 淘宝企业店铺如何注销
- 滇红茶的来历
- 滇红茶的品质特征
- 在天猫注册网店怎么注册 天猫如何注册店铺
- 正确饮用红茶,爽歪歪
- 如何测试电源的故障 测试电源好坏
- 普洱茶减肥妙方,如何喝普洱茶减肥
- 正确的红茶保存方法