shared_ptr 用法

匿名 (未验证) 提交于 2019-12-02 23:57:01

引入

shared_ptr 是c++为了提高安全性而添加的智能指针,方便了内存管理。

特点

shared_ptrshared_ptr

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_thiststd::shared_ptrptstd::shared_ptrpt1, pt2, ...ptt

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; }

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; }

shared_ptr的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。shared_ptr 的线程安全级别和内建类型、标准库容器、std::string 一样,即:

  1. 一个 shared_ptr 对象实体可被多个线程同时读取
  2. 两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作
  3. 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!