shared_ptr 用法
Posted 人生天地之间,若白驹之过隙,忽然而已
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shared_ptr 用法相关的知识,希望对你有一定的参考价值。
引入
shared_ptr 是c++为了提高安全性而添加的智能指针,方便了内存管理。
特点
shared_ptr
是通过指针保持对象共享所有权的智能指针。多个 shared_ptr
对象可占有同一对象。这便是所谓的引用计数(reference counting)。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。使得指针可以共享对象,并且不用考虑内存泄漏问题
shared_ptr 可以支持普通指针的所有操作,完全可以像操作普通指针一样操作智能指针。
shared_ptr 可以通过三种方式得到(拷贝初始化,定义delete操作的方式不在罗列,只讨论初始化指针所指对象来源):
1.通过一个指向堆上申请的空间的指针初始化(切记不要用栈上的指针,否则,当智能指针全部释放控制权(栈中的对象离开作用域本身就会析构一次),将会析构对象,导致出错)
2.通过make_shared函数得到
3.通过另外一个智能指针初始化
int *a = new int(8); std::shared_ptr<int> p1(a); auto b = std::make_shared<int>(2); auto c(b);
注意事项
1. 使用原生指针多次初始化
class Base { public: Base() { printf("con\n"); } ~Base() { printf("decon\n"); } }; int main() { Base *a = new Base(); std::shared_ptr<Base> p1(a); std::shared_ptr<Base> p2(a); return 0; }
这段代码,导致调用一次构造函数,两次析构函数
2. 使用一个 shared_ptr 的 get() 初始化另一个 shared_ptr
Base *a = new Base(); std::shared_ptr<Base> p1(a); std::shared_ptr<Base> p2(p1.get());
cppreference 指明这是未定义行为
3. 使用 shared_ptr 包装 this 指针
class Base { public: Base() { printf("con\n"); } ~Base() { printf("decon\n"); } std::shared_ptr<Base> sget() { return std::shared_ptr<Base>(this); } }; int main() { Base b; std::shared_ptr<Base> p = b.sget(); return 0; }
这会调用两次析构函数。一次是b对象析构时,一次是智能指针销毁时
正确的使用方法是:
class Base : public std::enable_shared_from_this<Base> { public: Base() { printf("con\n"); } ~Base() { printf("decon\n"); } std::shared_ptr<Base> sget() { return shared_from_this(); } }; int main() { std::shared_ptr<Base> b = std::make_shared<Base>(); std::shared_ptr<Base> a = b->sget(); return 0; }
enable_shared_from_this
能让其一个对象(假设其名为 t
,且已被一个 std::shared_ptr 对象 pt
管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ...
) ,它们与 pt
共享对象 t
的所有权。
4. shared_ptr 包装数组
shared_ptr 不能直接包装数组,需要指定数组的析构函数。不过 shared_ptr 不支持取下标操,unique_ptr 是支持的
class Base { public: Base() { printf("con\n"); } ~Base() { printf("decon\n"); } }; int main() { std::shared_ptr<Base> a(new Base[2], [] (Base* p) { delete[] p; }); return 0; }
5. 循环引用
class A; class B; using sa = std::shared_ptr<A>; using sb = std::shared_ptr<B>; class A { public: A() { printf("A con\n"); } ~A() { printf("A decon\n"); } sb b_; }; class B { public: B() { printf("B con\n"); } ~B() { printf("B decon\n"); } sa a_; }; int main(int argc, char const *argv[]) { sa a(new A); sb b(new B); a->b_ = b; b->a_ = a; return 0; }
对象 a 和 b 都未被析构
正确的方法是使用 weak_ptr 代替 shared_ptr
class A; class B; using sa = std::shared_ptr<A>; using sb = std::shared_ptr<B>; class A { public: A() { printf("A con\n"); } ~A() { printf("A decon\n"); } std::weak_ptr<B> b_; }; class B { public: B() { printf("B con\n"); } ~B() { printf("B decon\n"); } std::weak_ptr<A> a_; }; int main(int argc, char const *argv[]) { sa a(new A); sb b(new B); a->b_ = b; b->a_ = a; return 0; }
6. 多线程中使用 shared_ptr
shared_ptr的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。shared_ptr 的线程安全级别和内建类型、标准库容器、std::string 一样,即:
- 一个 shared_ptr 对象实体可被多个线程同时读取
- 两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作
- 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁
以上是关于shared_ptr 用法的主要内容,如果未能解决你的问题,请参考以下文章
C++笔记-auto_ptr&unique_ptr&shared_ptr&shared_ptr基本用法
C++笔记-auto_ptr&unique_ptr&shared_ptr&shared_ptr基本用法