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

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
    " ~  E' C/ b8 w, k! Z
shared_ptr
shared_ptr使用了引用计数,每一个shared_ptr的拷贝都指向相同的内存,每次拷贝都会触发引用计数+1,每次生命周期结束析构的时候引用计数-1,在最后一个shared_ptr析构的时候,内存才会释放。
使用方法如下:
6 ^# e& b+ c$ l# {% \4 c
    8 b, q9 d) e) {+ Y
    1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">
      9 s( Q) \: I" O; [& r7 Q
    2. struct ClassWrapper {
      7 a! \  e' u8 N. m# S8 \9 ?/ D
    3.     ClassWrapper() {
      8 E* G+ v( c/ N5 \
    4.         cout << "construct" << endl;; z& q. D- J7 {+ `
    5.         data = new int[10];: d3 A0 L' e, V8 i2 O7 d$ Y
    6.     }
      & G0 E  j! g* k+ |6 L
    7.     ~ClassWrapper() {
      " l: Q8 X( `: Q! e/ A" w3 y( [; V
    8.         cout << "deconstruct" << endl;
      ' H6 h) T$ k# `# ~
    9.         if (data != nullptr) {
      ) g. c2 m) U$ X, {( ~
    10.             delete[] data;3 t% N+ r: \: n' s& ~/ y6 l
    11.         }2 r( @8 ~- B7 J( W
    12.     }4 E; {+ M+ z2 p9 I1 p. A% d* `, o
    13.     void Print() {6 {1 ~# ]% U! Q3 p
    14.         cout << "print" << endl;
      ) w6 f: ~- C! J4 D8 P
    15.     }
      " Y: F( `( h3 x. }7 D* h- `% r
    16.     int* data;, |/ `& X" X) S; u1 V
    17. };5 b% c' ?4 |9 b9 A

    18. 4 P- Y1 {+ u+ L/ S/ ?' M7 N; L
    19. void Func(std::shared_ptr<ClassWrapper> ptr) {
      : W! z- p; a3 R3 h( F, z3 p
    20.     ptr->Print();
      * A3 R( M) h3 A, v: F
    21. }
      % M- S% B8 U9 F* h3 V

    22. ' Z& p4 l" G0 q! d3 {) A/ k, M
    23. int main() {0 I+ F9 l; e) _7 X8 k9 L/ }
    24.     auto smart_ptr = std::make_shared<ClassWrapper>();
      & `8 h( }, P0 s( g& r% X
    25.     auto ptr2 = smart_ptr; // 引用计数+1
      : l8 K2 b; F5 j4 ]2 e
    26.     ptr2->Print();2 Q3 o2 S4 r: |
    27.     Func(smart_ptr); // 引用计数+1  b$ p3 h8 Q. e+ p' O
    28.     smart_ptr->Print();( y6 O4 P+ q% z- j5 Z$ w
    29.     ClassWrapper *p = smart_ptr.get(); // 可以通过get获取裸指针) ^% j% b3 u) Z  T
    30.     p->Print();+ X/ D' Z5 I# q3 n
    31.     return 0;5 @; Q3 [1 ~$ K* T
    32. }</font></font></font>
    复制代码
  • 智能指针还可以自定义删除器,在引用计数为0的时候自动调用删除器来释放对象的内存,代码如下:
      T! R  E/ O+ m, h1 G6 r& s
  • std::shared_ptr<int> ptr(new int, [](int *p){ delete p; });
    ' ~# [7 W- ^4 {" l. K
关于shared_ptr有几点需要注意:
  J# A/ P2 N, @& e8 h
• 不要用一个裸指针初始化多个shared_ptr,会出现double_free导致程序崩溃
• 通过shared_from_this()返回this指针,不要把this指针作为shared_ptr返回出来,因为this指针本质就是裸指针,通过this返回可能 会导致重复析构,不能把this指针交给智能指针管理。
  1. , s! t5 S  J, H- j5 g9 t6 h# ~
  2. class A {
    4 j$ ]0 f$ B& P# A
  3.     shared_ptr<A> GetSelf() {
    $ U/ P. ]* M8 D) d
  4.        return shared_from_this();
    . F" Q: D/ {; J) C' I# X, i
  5.        // return shared_ptr<A>(this); 错误,会导致double free
    5 D5 b& A$ p0 w
  6.     }  7 p' r% Q8 P- h, n/ e0 d! X9 M3 G
  7. };
复制代码

! A3 K0 |7 w1 c4 Z* i
  • 尽量使用make_shared,少用new。
  • 不要delete get()返回来的裸指针。
  • 不是new出来的空间要自定义删除器。
  • 要避免循环引用,循环引用导致内存永远不会被释放,造成内存泄漏。

    1. . ?' U. x1 }0 j8 u) C3 f1 S0 G
    2. using namespace std;5 k- O" u8 g' F4 m
    3. struct A;
      ( h. h2 }: \* I( Q1 S+ U
    4. struct B;  N: N6 ~5 a7 ~

    5. 1 u9 [- R% s3 ?. {/ ?- y' P
    6. struct A {
      : F* X, d9 X  F4 G; X) M
    7.    std::shared_ptr<B> bptr;
      # Q9 o: I: g$ [, d) j) ]
    8.    ~A() {+ E6 H1 f8 s$ H% }" i0 |
    9.        cout << "A delete" << endl;8 w# K7 |/ \* b& L- U/ D
    10.   }* K& A5 ~' p3 R7 P: V
    11. };
      : v7 P3 J9 _! E0 U

    12. # C9 E5 u# M8 ~
    13. struct B {
      6 t: j! F4 f  X
    14.    std::shared_ptr<A> aptr;
      3 d$ n6 y% j/ \% p! J; X4 G
    15.    ~B() {8 l( \6 F/ d8 k, o
    16.        cout << "B delete" << endl;0 v  w) k1 Z4 ^3 ]' I6 ?/ P5 O
    17.    }
      7 k3 c5 l9 K, W+ N% F  c5 _
    18. };
      0 u0 \, g* ]5 e+ C6 j% ~

    19. - e; v5 n, R6 a, }
    20. int main() {% p* }; p7 r( R* d
    21.    auto aaptr = std::make_shared<A>();: ?1 T1 d8 W' |! d& s
    22.    auto bbptr = std::make_shared<B>();
      7 _7 K9 J) f9 j6 F* L) z8 c
    23.    aaptr->bptr = bbptr;& H4 p5 [. Y( q' k* m+ Q
    24.    bbptr->aptr = aaptr;
      # ^7 @1 o' v4 P6 W* u
    25.    return 0;/ K) [+ j& Q% z$ ^2 G1 m
    26. }
    复制代码

    8 ^: `4 y8 W. H7 a$ w
上面代码,产生了循环引用,导致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. 4 j( N$ O1 B1 m' J2 l2 K: D; |
    2. struct A;& k; l* A! W7 S
    3. struct B;/ n( r' o- ]3 _; o) |: a! f2 w
    4. 7 V$ H9 R* z; u) `
    5. struct A {
      ; F! K; f' l, }0 v4 E
    6.    std::shared_ptr<B> bptr;& m. H% p' d0 f8 n
    7.    ~A() {, n; _( E- M- P0 V) u9 n, \: q
    8.        cout << "A delete" << endl;8 z# j: D) ^7 X( t4 w; R
    9.    }/ ?; y# M4 ~" J
    10.    void Print() {: L" ~$ K8 P" @* @
    11.        cout << "A" << endl;
      - z) ?+ P5 F9 [/ h8 l: S
    12.    }0 Q7 P1 G* E! N
    13. };
      / f2 s" g6 ^; b0 \$ _6 `

    14. 2 P! E9 f* U. o7 _( g, Q! {- R8 p
    15. struct B {
      . W3 H% m" h4 v" U+ w4 U# S
    16.    std::weak_ptr<A> aptr; // 这里改成weak_ptr3 O5 a' k+ A! Z3 I+ h8 J
    17.    ~B() {( I0 j6 c8 x# @' B) v" _$ ?2 [& R
    18.        cout << "B delete" << endl;
      9 v8 T5 T- R/ [! w" G5 a% ~  ]
    19.    }: o/ J5 G2 K( L
    20.    void PrintA() {) d3 o! _& ]3 F) H! c+ E; G, M
    21.        if (!aptr.expired()) { // 监视shared_ptr的生命周期
      * F, f: R3 G& q- M, A; j' M
    22.            auto ptr = aptr.lock();( X2 U% t" A  {+ d! j* P& x
    23.            ptr->Print();
      7 N' @5 Z+ n) M. f) n
    24.       }2 T7 j! ]2 P" [
    25.    }
      " F" |9 x# {* w/ t6 C
    26. };6 J# j/ s; h+ T+ p
    27. 1 m7 I4 K2 v6 R/ L: [$ Q
    28. int main() {: R2 Z; t/ M' W/ s& H: S% @9 l, V# m
    29.    auto aaptr = std::make_shared<A>();9 @/ K; t! E1 [2 g2 M: {
    30.    auto bbptr = std::make_shared<B>();' e9 [- }7 q; k& w1 E
    31.    aaptr->bptr = bbptr;
      4 j1 @: B8 U% I* c: w( y+ q
    32.    bbptr->aptr = aaptr;
      ) l7 s0 [/ `( R
    33.    bbptr->PrintA();6 j: @* f& L8 C: h% o3 D# ?
    34.    return 0;
      & G1 g0 _2 ?# _  a# K/ Z
    35. }) w1 n( L+ n% Y3 j2 q
    36. 输出:
      ! g" z- n( L( ]$ H* Z8 i
    37. A
      9 c4 _4 E% c9 j
    38. A delete
      ! ]4 G- f7 e: ~$ p/ k, q
    39. B delete
    复制代码
    - X4 V$ \* l3 |
unique_ptr
std::unique_ptr是一个独占型的智能指针,它不允许其它智能指针共享其内部指针,也不允许unique_ptr的拷贝和赋值。使用方法和shared_ptr类似,区别是不可以拷贝:

  1. % h' F. l* m" f9 W8 i8 b
  2. using namespace std;1 L3 F- O# ?. S- I2 Q  D( y8 J
  3. 5 q3 g: M) ~- F7 B. t( N
  4. struct A {
      J$ t. U0 o) k7 W6 s/ [/ Z" a
  5.    ~A() {
    7 A; U: u1 G; l
  6.        cout << "A delete" << endl;; ?& w+ r6 T/ z! K4 ]
  7.    }& s5 V/ m" q4 ~* P, a" u( q  y: Q
  8.    void Print() {
    9 g0 R, z, }- U5 w% b/ k5 C2 K- u
  9.        cout << "A" << endl;
    5 D- n8 Y! f: a
  10.    }
    4 v, D2 i1 f$ W  V" y
  11. };
    . _/ P" w  d* b* o0 Z

  12. : X0 t, ?* Y8 N: a4 ]

  13. & ~4 t7 B" l6 H. r- X1 ?2 x
  14. int main() {- h* J! [1 t) k$ l9 D
  15.    auto ptr = std::unique_ptr<A>(new A);
    % A3 ^  Z4 `- O' z0 Y3 |
  16.    auto tptr = std::make_unique<A>(); // error, c++11还不行,需要c++14
    9 Q# N. g6 v9 Y8 `% L! M
  17.    std::unique_ptr<A> tem = ptr; // error, unique_ptr不允许移动
    1 y# F. r. F6 }; Q4 j: p+ b
  18.    ptr->Print();
    * }- S5 c9 q1 h/ ]' ^+ I5 v% `
  19.    return 0;/ X6 g% F6 v7 ^+ F% ?
  20. }
复制代码
: v9 |, R$ K( q7 o2 |( J1 a
unique_ptr也可以像shared_ptr一样自定义删除器,使用方法和shared_ptr相同。
关于c++11的智能指针的使用就介绍到这里

. V# e- h: F% ~0 \1 ?3 Y. J9 M5 F9 X
收藏 评论0 发布时间:2020-7-6 22:48

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版