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 對象,那么需要加鎖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM