你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

c++11引入了三种智能指针

[复制链接]
gaosmile 发布时间:2020-7-6 22:48
很多人谈到c++,说它特别难,可能有一部分就是因为c++的内存管理吧,不像java那样有虚拟机动态的管理内存,在程序运行过程中可能就会出现内存泄漏,然而这种问题其实都可以通过c++11引入的智能指针来解决,相反我还认为这种内存管理还是c++语言的优势,因为尽在掌握。
c++11引入了三种智能指针:
  • std::shared_ptr
  • std::weak_ptr
  • std::unique_ptr
    2 G) z) @8 q8 K
shared_ptr
shared_ptr使用了引用计数,每一个shared_ptr的拷贝都指向相同的内存,每次拷贝都会触发引用计数+1,每次生命周期结束析构的时候引用计数-1,在最后一个shared_ptr析构的时候,内存才会释放。
使用方法如下:
  p8 M7 a0 c' v4 g/ g  ~
    ! o$ O; \  @; n" k
    1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">+ [- c) u5 n5 @. ?) L, M; Q7 j% e
    2. struct ClassWrapper {& i  e  [' ~" F
    3.     ClassWrapper() {! O# M+ e- g7 `+ E+ b6 @2 T# y( l
    4.         cout << "construct" << endl;
        _+ |4 N9 K6 J: `3 b( c2 L4 F! ^
    5.         data = new int[10];
        b7 L# D( Z" @% n/ H
    6.     }' N* l& s1 q* Y' Y5 \
    7.     ~ClassWrapper() {$ J1 z3 O4 l: S4 J  w9 n
    8.         cout << "deconstruct" << endl;
      3 E1 F( V- E4 L. v, E) t
    9.         if (data != nullptr) {* D* B  f; H2 E, V0 U
    10.             delete[] data;
      ; _; t5 @! p. f6 d3 @  }
    11.         }' m; c2 I! k4 W; `, q
    12.     }0 @8 _6 Z0 S  Y) |
    13.     void Print() {
      $ M6 ^# G) y# G; z& q% a
    14.         cout << "print" << endl;& i4 Z$ G# `' ]6 O. I  ]. h: g6 i
    15.     }, f( a' {& A+ h$ m; R7 D
    16.     int* data;
      , m5 y* Y7 i, K# J" l
    17. };
      ( n! p- ]3 a; i* i: \$ c3 F3 ]
    18.   y3 n5 ]. Y- B0 b
    19. void Func(std::shared_ptr<ClassWrapper> ptr) {3 W: e+ \6 Y& A. |; _& H3 D
    20.     ptr->Print();, `8 Q8 N) B6 h& T$ F8 n$ {
    21. }
      9 }- t* v' `* u% \1 ^( g

    22. 3 C. D* [' s, u; P; R6 _  ^( q. _
    23. int main() {
      * t2 [' R  G, m: p# R, P
    24.     auto smart_ptr = std::make_shared<ClassWrapper>();4 N8 n% G: z. S) m' x/ d3 V$ \
    25.     auto ptr2 = smart_ptr; // 引用计数+1
      * l$ O# g2 I' d; ]5 t9 g. G
    26.     ptr2->Print();
      $ d3 G) \# w' Z2 s; a' Q
    27.     Func(smart_ptr); // 引用计数+1/ F" W% C' J& s9 S. A& B
    28.     smart_ptr->Print();6 j+ v1 q. \- }$ Y* F
    29.     ClassWrapper *p = smart_ptr.get(); // 可以通过get获取裸指针
        u% |- E7 {1 N9 t/ ?$ M" X* ]) X
    30.     p->Print();
      ( \5 F+ ?0 W3 n& F
    31.     return 0;( y4 U% f9 S: R5 k- j- N
    32. }</font></font></font>
    复制代码
  • 智能指针还可以自定义删除器,在引用计数为0的时候自动调用删除器来释放对象的内存,代码如下:
    ; [2 X5 _, O  |
  • std::shared_ptr<int> ptr(new int, [](int *p){ delete p; });
    ' E) W" F8 u- P
关于shared_ptr有几点需要注意:
9 H  g: r* M" H
• 不要用一个裸指针初始化多个shared_ptr,会出现double_free导致程序崩溃
• 通过shared_from_this()返回this指针,不要把this指针作为shared_ptr返回出来,因为this指针本质就是裸指针,通过this返回可能 会导致重复析构,不能把this指针交给智能指针管理。

  1. 3 |: M  z! i0 Y/ [. Z
  2. class A {4 D: c0 q' @( S9 U
  3.     shared_ptr<A> GetSelf() {
    0 b7 ~5 Z9 q' V' ~+ H0 U8 B
  4.        return shared_from_this();
    & F  z% u" l$ t
  5.        // return shared_ptr<A>(this); 错误,会导致double free
    7 x8 h( s' i+ q
  6.     }  
    # f. X) M! C! h" }4 s" [
  7. };
复制代码

) ]" U+ b( X3 I
  • 尽量使用make_shared,少用new。
  • 不要delete get()返回来的裸指针。
  • 不是new出来的空间要自定义删除器。
  • 要避免循环引用,循环引用导致内存永远不会被释放,造成内存泄漏。
    1. 2 s- y* e3 i& c3 ?$ D$ e
    2. using namespace std;- F% Q) e  L/ m$ q8 Q" L
    3. struct A;
      1 y3 N; R, F" s
    4. struct B;6 @  ^) x6 Y  |& R1 _5 K

    5. # o7 O9 K  W7 S* X# g& B
    6. struct A {! F* I* J" I3 v3 X
    7.    std::shared_ptr<B> bptr;
      + _* k6 I' u& q  A- _
    8.    ~A() {
      ( q, z6 ~& |! D4 _% |# v
    9.        cout << "A delete" << endl;' h1 f' x% }: y2 E  y
    10.   }
      . w1 W* Z3 R% @
    11. };
      4 w# N! K5 _3 A% y3 B4 m2 i3 Q
    12. / a2 s( l$ {4 m+ L
    13. struct B {6 d6 `0 c9 a) {0 ]
    14.    std::shared_ptr<A> aptr;
      ; `  q9 k- h$ ]9 o! Z
    15.    ~B() {
      6 R  n$ N0 ^9 k; q8 F- X0 F
    16.        cout << "B delete" << endl;
      * k! }) O- z- K! y8 Z' V
    17.    }3 |3 w0 o, K1 R5 U' X3 N6 @$ G3 m& \
    18. };
      , F" E4 N* b) G5 W& E: j& [' u
    19. & G) u; u( N3 Z. |. r% p
    20. int main() {! q+ G6 z) }3 u$ M& [) {, w
    21.    auto aaptr = std::make_shared<A>();
      $ `1 c$ G8 @% w' K+ z. G8 E
    22.    auto bbptr = std::make_shared<B>();$ [1 R6 g9 q5 ?9 Z% {
    23.    aaptr->bptr = bbptr;
      1 c( `2 X) ?3 K* o5 W
    24.    bbptr->aptr = aaptr;# @% ~* N) r8 Z9 N& o; [9 h& l
    25.    return 0;* g0 }& m+ z( q$ g! G$ D
    26. }
    复制代码
    4 W$ h& ]$ O2 g# @
上面代码,产生了循环引用,导致aptr和bptr的引用计数为2,离开作用域后aptr和bptr的引用计数-1,但是永远不会为0,导致指针永远不会析构,产生了内存泄漏,如何解决这种问题呢,答案是使用weak_ptr。
weak_ptr
weak_ptr是用来监视shared_ptr的生命周期,它不管理shared_ptr内部的指针,它的拷贝的析构都不会影响引用计数,纯粹是作为一个旁观者监视shared_ptr中管理的资源是否存在,可以用来返回this指针和解决循环引用问题。
  • 作用1:返回this指针,上面介绍的shared_from_this()其实就是通过weak_ptr返回的this指针,这里参考我之前写的源码分析shared_ptr实现的文章,最后附上链接。
  • 作用2:解决循环引用问题。
    1.   o: r) y, J; Y# e4 G- B" g/ R6 X
    2. struct A;
      ; K5 B6 f7 x" R: _
    3. struct B;+ t+ S1 q! n9 J& T( g" T% R2 c

    4. 2 H* V! @' A- ?! z$ f6 Y' C
    5. struct A {; h" _. r$ N& Y+ H' h
    6.    std::shared_ptr<B> bptr;
      ) n( U) J  ?( [
    7.    ~A() {: W1 ~/ ~& [$ S; L- }
    8.        cout << "A delete" << endl;
      % a7 U7 Y& p- N  L
    9.    }0 q( H, B! x0 S: N/ c" x
    10.    void Print() {! s6 P6 u. M% y
    11.        cout << "A" << endl;1 K6 \3 I8 L. U$ N
    12.    }- g' D! C7 z5 l1 }: k2 p
    13. };6 r6 a7 f3 i/ z% l) l/ F  E  [& M8 w

    14. & B; Y! J8 S' L4 u$ t! p
    15. struct B {
      ! b6 C- }  y( T6 L+ h2 ?/ q
    16.    std::weak_ptr<A> aptr; // 这里改成weak_ptr
      $ P) v+ {$ E. C, o
    17.    ~B() {1 h8 B& t2 S8 D; _
    18.        cout << "B delete" << endl;
      " m! J; H/ a7 |6 Z& y1 |! U
    19.    }
      / v7 x( B5 `. `4 U0 n8 x' z
    20.    void PrintA() {* [! P" j: B2 l2 i
    21.        if (!aptr.expired()) { // 监视shared_ptr的生命周期: B, Z- F. j5 K5 u  d5 I; N9 Z
    22.            auto ptr = aptr.lock();
      : \. I0 M; A, _* |7 a9 F) X
    23.            ptr->Print();
      + u5 t/ ~% G8 R3 t
    24.       }" A( M) r) z8 m% k
    25.    }' ^& M( a/ \& [% {" {% ^, F1 y1 y
    26. };
      : E. M& Y, s, U2 Y  ?% d

    27. ! C. Z- h6 L- L7 ^0 w
    28. int main() {& f- }% o6 J) [( M: d: s0 s) c. S
    29.    auto aaptr = std::make_shared<A>();: c# c- i% d+ u& _0 D
    30.    auto bbptr = std::make_shared<B>();% o+ [+ |- M, J% l5 ]* p
    31.    aaptr->bptr = bbptr;
      1 g0 T8 r4 r  a( E* j
    32.    bbptr->aptr = aaptr;
      " M. G) N6 Y1 W; P4 G
    33.    bbptr->PrintA();
      * y3 O& z+ K0 W
    34.    return 0;1 m" s0 r7 Y+ `2 N0 f0 Y; w% ]
    35. }
      3 @7 d$ h6 W4 b6 Z) i, ?4 I. l
    36. 输出:& N* X4 m; x7 i- V
    37. A* a8 C9 X1 e8 z$ M9 z! R" m
    38. A delete
      7 `" B9 K0 x  s* L) N
    39. B delete
    复制代码
    - u9 Z( H, ~) M! N2 X. I1 q
unique_ptr
std::unique_ptr是一个独占型的智能指针,它不允许其它智能指针共享其内部指针,也不允许unique_ptr的拷贝和赋值。使用方法和shared_ptr类似,区别是不可以拷贝:
  1. ! s, a: \8 U3 l. T
  2. using namespace std;0 Y0 M1 J' b! [- P' p

  3. 0 L6 T* @3 |2 w
  4. struct A {
    0 ?7 k( U- f% y# s- v4 Y8 F$ w
  5.    ~A() {
      x0 m! }+ T' N  m" H1 b
  6.        cout << "A delete" << endl;
    # N+ q! b& d  Z. R0 H
  7.    }
    ; n  O1 f5 c' B1 \  A8 v* k
  8.    void Print() {! t9 P( Q. H& u" X3 y
  9.        cout << "A" << endl;' J3 F/ L9 j$ `0 H% H. ]  @
  10.    }
    : P( J% j' D6 ^. E$ H6 ]5 f
  11. };  z3 z/ |$ D7 c7 \; E

  12. 8 [. J, U5 t2 v
  13. 0 Q5 D8 [4 P9 r
  14. int main() {9 m$ W8 _6 F* h' `9 q* u- ]5 X
  15.    auto ptr = std::unique_ptr<A>(new A);
    5 i% P6 X9 I  Y
  16.    auto tptr = std::make_unique<A>(); // error, c++11还不行,需要c++14
    . D$ Q/ n) l1 y
  17.    std::unique_ptr<A> tem = ptr; // error, unique_ptr不允许移动
    * l1 u7 O, E4 v, }$ |# w& Q# |  r
  18.    ptr->Print();3 P# N2 c% k! B7 U% W0 q
  19.    return 0;! O) n. D. v& H4 d6 z$ ~
  20. }
复制代码

: T  P0 g, e  J3 R4 l4 ^
unique_ptr也可以像shared_ptr一样自定义删除器,使用方法和shared_ptr相同。
关于c++11的智能指针的使用就介绍到这里
( J. ^4 t3 G# _0 `8 b' b" N& T
赞 收藏 评论0 发布时间:2020-7-6 22:48

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版