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

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

    1 c; g' u9 W. w8 f' K  s
shared_ptr
shared_ptr使用了引用计数,每一个shared_ptr的拷贝都指向相同的内存,每次拷贝都会触发引用计数+1,每次生命周期结束析构的时候引用计数-1,在最后一个shared_ptr析构的时候,内存才会释放。
使用方法如下:" D# B8 W1 d+ U5 Y
    . g" K' e3 k$ }
    1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black"># E5 ]" Z' O: l' O
    2. struct ClassWrapper {! y" x; k3 L/ e) H0 f' V
    3.     ClassWrapper() {0 E& M  }4 z9 x; r
    4.         cout << "construct" << endl;1 B8 I4 M4 Y" k' d
    5.         data = new int[10];
      ( ]2 x1 ~5 m. t3 U1 i1 s8 P3 U
    6.     }0 W" V) d+ ~  E" I0 o: d2 D
    7.     ~ClassWrapper() {
      # n5 l* X- j: G- f3 T  i
    8.         cout << "deconstruct" << endl;
      , f0 Z0 q) }. k' G9 p( }7 Z" j
    9.         if (data != nullptr) {/ U' ]" @$ C2 l
    10.             delete[] data;
      7 l8 \4 f8 ]3 y+ @: \
    11.         }
      0 I) g$ G' X8 g4 w/ C8 x
    12.     }- o1 e# C* L& d8 X. D9 J
    13.     void Print() {
      ) p6 g8 }) y' Q9 Q! v! M; o# J
    14.         cout << "print" << endl;( w) m) M6 C2 a. t; i
    15.     }
      # ^: X: v7 L* K( A$ @0 T
    16.     int* data;
      . T2 S( r* Z/ O5 Y- N, A7 w
    17. };
      0 a: x; M, m/ x; s

    18.   F# h7 a; J* W1 M, M( {; a/ H! O
    19. void Func(std::shared_ptr<ClassWrapper> ptr) {# s& e/ p/ f% K1 u1 l$ Y2 G4 }# M6 V
    20.     ptr->Print();
      ) e  y# R! {0 Y  o: M
    21. }* z7 l, z* m$ ~+ C: B: n

    22. $ R2 I* q4 J2 D! u* m
    23. int main() {6 S9 w  b" l: v  H; T5 t
    24.     auto smart_ptr = std::make_shared<ClassWrapper>();
      " t* D1 D4 |3 f& X# e! K% G7 _
    25.     auto ptr2 = smart_ptr; // 引用计数+15 m* o4 N+ g6 G# D/ e0 x
    26.     ptr2->Print();
      + F1 Z2 o% y$ s) [; A9 a) n9 L9 j
    27.     Func(smart_ptr); // 引用计数+1
      , ^' V) Q# _9 o, M5 x+ t
    28.     smart_ptr->Print();& P' G1 K0 C! D8 T
    29.     ClassWrapper *p = smart_ptr.get(); // 可以通过get获取裸指针5 P; u. J5 w7 F4 {# d" b  s
    30.     p->Print();
      / s; ~$ p% y4 _: b
    31.     return 0;
      & F4 |& W9 G! l& ]) b7 l5 V
    32. }</font></font></font>
    复制代码
  • 智能指针还可以自定义删除器,在引用计数为0的时候自动调用删除器来释放对象的内存,代码如下:- v- S; |. S0 A; D# [' f! m
  • std::shared_ptr<int> ptr(new int, [](int *p){ delete p; });$ U: P0 ?1 P3 s; {
关于shared_ptr有几点需要注意:
. E1 A5 R8 w+ R" A
• 不要用一个裸指针初始化多个shared_ptr,会出现double_free导致程序崩溃
• 通过shared_from_this()返回this指针,不要把this指针作为shared_ptr返回出来,因为this指针本质就是裸指针,通过this返回可能 会导致重复析构,不能把this指针交给智能指针管理。
  1. 4 N# {  b; [, y/ [5 s  r
  2. class A {
    0 ]  p2 e) \  i9 g9 C4 l0 U
  3.     shared_ptr<A> GetSelf() {/ a+ Q0 F6 A8 O
  4.        return shared_from_this();$ N5 X0 J+ o3 S5 x4 r
  5.        // return shared_ptr<A>(this); 错误,会导致double free! O" b4 m) Q, q, n. u
  6.     }  
    1 F* y* r3 Z7 K3 ~# z  C
  7. };
复制代码

# z" _) G% W  ]( ]% ~8 U+ U6 x" m; D; J! O
  • 尽量使用make_shared,少用new。
  • 不要delete get()返回来的裸指针。
  • 不是new出来的空间要自定义删除器。
  • 要避免循环引用,循环引用导致内存永远不会被释放,造成内存泄漏。

    1. 4 ]' U3 A: ^9 f/ K5 W8 ]' Z1 U
    2. using namespace std;
      5 z1 y, M. Q9 F  t. R
    3. struct A;6 B! m# s; v1 g8 P
    4. struct B;& f  U) Q) a7 M

    5. 1 Q  G& V! u2 U& U, I9 b
    6. struct A {
      : Q% F. c* x) |6 \; I" y  f
    7.    std::shared_ptr<B> bptr;' m) l8 |' @0 a: s- a
    8.    ~A() {" u+ u' N- I+ S
    9.        cout << "A delete" << endl;
      / H* h( Z) O* P
    10.   }5 z: b6 h$ j" w( p0 v# Q
    11. };
      ! M$ e1 h  k1 Y4 k1 k- a9 M( Q# h
    12. ( d3 W9 N* L# f; p: T6 e
    13. struct B {/ ]$ C0 v# K0 {0 m. `) ?
    14.    std::shared_ptr<A> aptr;
      & M, k9 [. C# ?& z% N
    15.    ~B() {
      - m; X9 g* l7 H. d. b$ h9 D
    16.        cout << "B delete" << endl;
      & g: G/ T+ h) j2 z! [$ ]; u. |
    17.    }
      : N  _! f/ B, H: T8 G* R6 t
    18. };
      4 t1 s' Z* B$ k6 I2 V
    19. % T" |4 c% b: W
    20. int main() {
      ( [; w8 Y, _; M; {5 w% A1 [0 g
    21.    auto aaptr = std::make_shared<A>();' D7 ^# n! E( P! {8 \4 U
    22.    auto bbptr = std::make_shared<B>();
      * G  l# o& O/ `- x/ K
    23.    aaptr->bptr = bbptr;! W% A. C8 b% w$ j' D( Q0 ?& U. ]
    24.    bbptr->aptr = aaptr;$ }7 I) D# ?7 i+ o  K" j, S: W  [
    25.    return 0;
      % j; {$ z1 R; }! O5 L  k4 }
    26. }
    复制代码

    ' y/ H' j6 s- c0 v+ J. h/ G  V8 s
上面代码,产生了循环引用,导致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. ( W! b# r- O! n1 B1 H/ m" o
    2. struct A;& z/ }+ J" k) u+ Q
    3. struct B;2 C) S% O; j4 u
    4. 7 f8 O. v0 n7 g! ~' S9 L& N
    5. struct A {! C7 _* R# }6 j" s( o
    6.    std::shared_ptr<B> bptr;' O9 `% o" o- Z
    7.    ~A() {
      9 y: @& K  o5 B$ m9 A0 L  V9 @& ]# [3 C
    8.        cout << "A delete" << endl;
      7 I: B1 O2 d: v+ l4 W
    9.    }
      $ d/ m4 z. R0 f9 N
    10.    void Print() {
      # e, j+ _8 O$ A2 D/ c! M4 W4 @+ h! `
    11.        cout << "A" << endl;
      ( t( B, z: D) K/ D
    12.    }
      9 F: Z$ A1 L9 P5 q# c9 s' A/ Q
    13. };
      : i$ a& u; g9 o2 q9 j8 G
    14. * @/ k# k' M8 U: E% K6 G; ]5 n: z
    15. struct B {
      + o$ h* e& A, I( C) q
    16.    std::weak_ptr<A> aptr; // 这里改成weak_ptr) X, F# h2 ~$ v: v5 d
    17.    ~B() {
      8 ?. \% I( b. F- y! R/ [( M6 i
    18.        cout << "B delete" << endl;# m5 h6 j5 s/ x( d8 h+ m0 W3 L
    19.    }
      9 A5 o7 a; u+ I9 E! i0 t& H
    20.    void PrintA() {
      ) S1 t' X7 Q! {
    21.        if (!aptr.expired()) { // 监视shared_ptr的生命周期
      ' _- C' E9 a/ l) l8 J2 u
    22.            auto ptr = aptr.lock();
      ' [! p+ S( B$ U) F
    23.            ptr->Print();1 z2 j9 U; ]( H- x
    24.       }6 O! D! n8 Q: [2 e* t) P
    25.    }  H3 I1 P6 q# j; {: _8 v
    26. };% ^. K0 J& k  K) z/ H
    27. & o* N- H% }! d8 E/ B
    28. int main() {
      8 ~  C; ^7 V( L" Q
    29.    auto aaptr = std::make_shared<A>();
      / {! P7 f8 U0 `$ A2 o4 A8 U
    30.    auto bbptr = std::make_shared<B>();
      + x9 P! Y( ^4 x) a% I
    31.    aaptr->bptr = bbptr;
      4 `& L, J5 d5 ], ]& n/ S
    32.    bbptr->aptr = aaptr;0 f6 F, y, C  B$ ^
    33.    bbptr->PrintA();4 f/ T9 _; s9 D. w; X; v
    34.    return 0;. p) o- ^1 p% h/ [( _# E7 G
    35. }' t. V: Z2 U+ W# Y% N
    36. 输出:/ W- }3 y' U5 U) H
    37. A/ x. N- n- j5 L4 L
    38. A delete0 \/ g! q, V: Z6 [3 _: ^
    39. B delete
    复制代码

      L, u7 l# t7 ^& k( M/ i$ D. u6 `
unique_ptr
std::unique_ptr是一个独占型的智能指针,它不允许其它智能指针共享其内部指针,也不允许unique_ptr的拷贝和赋值。使用方法和shared_ptr类似,区别是不可以拷贝:

  1. 5 L/ g0 e' Y) q) \7 ~6 a
  2. using namespace std;! O3 T- ~; z* G1 u* E+ Y: V
  3. : V1 Q1 F/ {1 p5 n: Z& r6 N
  4. struct A {
    ; B& n1 B% n) o6 t& D
  5.    ~A() {
    3 o& A' }, J$ c8 m/ g  c
  6.        cout << "A delete" << endl;
    : s/ e% i8 P& W
  7.    }) G$ K& v4 ^2 n$ r
  8.    void Print() {* W- G, d) ^# K) {# f& a/ E
  9.        cout << "A" << endl;
    ! ^. @  U  M4 ~' @" B
  10.    }: E# R* s, \/ N* G: u+ x* P
  11. };! J# _# J) Z1 W( I9 d, Q

  12. ! D+ K3 Z3 H' f# c4 K- B

  13. " R7 D  t! n& ?4 X/ H3 F, B
  14. int main() {
    $ u( ^- U' x! K/ K1 y
  15.    auto ptr = std::unique_ptr<A>(new A);% g' e% J* n5 s1 M  G" M! n
  16.    auto tptr = std::make_unique<A>(); // error, c++11还不行,需要c++145 o% d5 I! G" S: @- p" V/ ]
  17.    std::unique_ptr<A> tem = ptr; // error, unique_ptr不允许移动4 {) y3 J, H2 D% T" ^2 v
  18.    ptr->Print();
    0 u) E6 K5 B! G# M/ V
  19.    return 0;6 R! X: U  K3 {
  20. }
复制代码
& A! H: j- K) D6 @; l/ P
unique_ptr也可以像shared_ptr一样自定义删除器,使用方法和shared_ptr相同。
关于c++11的智能指针的使用就介绍到这里
: Z5 R1 [( k' E
赞 收藏 评论0 发布时间:2020-7-6 22:48

举报

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