目录
C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。程序员自己管理堆内存可以提高了程序的效率,但是整体来说堆内存的管理是麻烦的,C++11中引入了智能指针的概念,方便管理堆内存。使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题等,使用智能指针能更好的管理堆内存。 理解智能指针需要从下面三个层次: 1、从较浅的层面看,智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装,这使得智能指针实质是一个对象,行为表现的却像一个指针。 2、智能指针的作用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。另外指针的释放时机也是非常有考究的,多次释放同一个指针会造成程序崩溃,这些都可以通过智能指针来解决。 3、智能指针还有一个作用是把值语义转换成引用语义。C++和Java有一处最大的区别在于语义不同,在Java里面下列代码:
你当然知道,这里其实只生成了一个对象,a和b仅仅是把持对象的引用而已。但在C++中不是这样,
这里却是就是生成了两个对象。 2、智能指针的使用智能指针在C++11版本之后提供,包含在头文件中,shared_ptr、unique_ptr、weak_ptr 2.1 shared_ptr的使用shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。
#include <memory> int main() { {2 ]6 k) s5 A) o int a = 10; std::shared_ptr<int> ptra = std::make_shared<int>(a);1 C/ Y) i( r6 I9 i. S. y9 T std::shared_ptr<int> ptra2(ptra); //copy' w4 H8 z& @" ?( ]$ L std::cout << ptra.use_count() << std::endl; int b = 20; int *pb = &a;+ z* n \# G' r: K$ N //std::shared_ptr<int> ptrb = pb; //error std::shared_ptr<int> ptrb = std::make_shared<int>(b); ptra2 = ptrb; //assign" E# L$ R4 h+ i0 w" V, ^# y9 p pb = ptrb.get(); //获取原始指针 std::cout << ptra.use_count() << std::endl;" d* T Z7 O# D) s$ s6 u0 L std::cout << ptrb.use_count() << std::endl;1 l3 y+ r7 V. x3 ?! \3 f }$ b+ E9 O5 r: Q2 k" X }) e% h, J/ }0 N4 o$ }" c: @! L 2.2 unique_ptr的使用 unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。相比与原始指针unique_ptr用于其RAII的特性,使得在出现异常的情况下,动态资源能得到释放。unique_ptr指针本身的生命周期:从unique_ptr指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作)。unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。 #include <iostream># x% n6 x- {1 G+ N, r#include <memory>; Q% I+ Z) Y3 s- k 6 @, h( o0 Z" g. a# l int main() { { std::unique_ptr<int> uptr(new int(10)); //绑定动态对象 //std::unique_ptr<int> uptr2 = uptr; //不能賦值6 W, [( s1 D* h //std::unique_ptr<int> uptr2(uptr); //不能拷貝 std::unique_ptr<int> uptr2 = std::move(uptr); //轉換所有權5 p" i1 ?+ w( q7 K uptr2.release(); //释放所有权 }& r2 X3 l+ x: W' E; Q$ }1 I //超過uptr的作用域,內存釋放% a) k7 O3 \4 g% F( l! c }$ {: w; q1 o" X# Y4 O r! j 2.3 weak_ptr的使用 weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。 #include <iostream>$ o. b2 P2 w, e9 p$ o#include <memory> / z& K! a7 b: ^! H0 G6 H int main() { {" C+ }# Y" s6 [: L. H9 e2 d. U" ^; F std::shared_ptr<int> sh_ptr = std::make_shared<int>(10); std::cout << sh_ptr.use_count() << std::endl; std::weak_ptr<int> wp(sh_ptr); std::cout << wp.use_count() << std::endl;" Q5 P8 V( r+ l if(!wp.expired()){ std::shared_ptr<int> sh_ptr2 = wp.lock(); //get another shared_ptr% b7 Y& _9 @4 e6 F R *sh_ptr = 100;: R- W& U- j9 q0 `% v2 c( X2 G' r std::cout << wp.use_count() << std::endl;9 S. P) \8 [' p! [7 }1 Q } } //delete memory }% c, P$ g2 C; p2 Q& w" _ 2.4 循环引用 考虑一个简单的对象建模——家长与子女:a Parent has a Child, a Child knowshis/her Parent。在Java 里边很好写,不用担心内存泄漏,也不用担心空悬指针,只要正确初始化myChild 和myParent,那么Java 程序员就不用担心出现访问错误。一个handle 是否有效,只需要判断其是否non null。 public class Parent{- F7 n$ x' h. L; X/ _ private Child myChild;1 R) c5 P6 l. a3 `5 W }* C0 ~ I+ y: A. L public class Child0 m% B& {3 c, L* G6 }) T4 y8 }: o {' ]3 b( C2 |. Z9 j5 z# o private Parent myParent; }, o& k! ?5 n, Z, w 在C++里边就要为资源管理费一番脑筋。如果使用原始指针作为成员,Child和Parent由谁释放?那么如何保证指针的有效性?如何防止出现空悬指针?这些问题是C++面向对象编程麻烦的问题,现在可以借助smart pointer把对象语义(pointer)转变为值(value)语义,shared_ptr轻松解决生命周期的问题,不必担心空悬指针。但是这个模型存在循环引用的问题,注意其中一个指针应该为weak_ptr。 原始指针的做法,容易出错 #include <iostream>1 m# G" A+ n/ u+ R' ~+ |( ?! m, p9 r#include <memory> 5 e5 ?: i) F9 r class Child;, Y* I7 Z: W; y3 Z6 ~: ?8 u class Parent;/ ?/ u# U8 k1 ^$ }! } ! P" T; ?* M4 U5 N class Parent {! Y' f2 O1 d' O8 d$ \2 @+ Z private: Child* myChild; public:/ m0 g& h, ]' J2 N void setChild(Child* ch) { this->myChild = ch; } void doSomething() { if (this->myChild) {& i. X1 r' p+ L- \+ g4 j9 q$ E } } . n: G: @* |) ?' f0 O5 k: [/ @4 Z; T ~Parent() { delete myChild;4 U7 r3 o# R2 C! _. m6 g }# B( B+ {% a5 C* o- { };& X* t) j. V/ i; M8 \ class Child { private: Parent* myParent; public: void setPartent(Parent* p) { this->myParent = p;$ C) r5 f; ^- n4 T" a1 B$ ? }- v: h3 \' t+ Z! X void doSomething() { if (this->myParent) {( \: N/ Q6 f+ A, o( i b }. N5 F1 F5 {. W4 ~6 t0 o" ^8 J" E1 s }( \% v6 }3 n3 W% F. V! h0 F- P ~Child() {& w+ j# R, h* E" h delete myParent; }) ~3 v* F& X' u( W( C( J3 w };. k; ~ y# b r! @0 C N - ^# b# r+ m8 m f" H' Y int main() { {) d5 `& s3 @. U; O8 L4 _1 v Parent* p = new Parent;; R; f7 c. \$ |1 Q% e Child* c = new Child;5 q8 G; u p. C. l5 `/ A p->setChild(c);5 `9 R, D' p# J* D3 G% r, Y) _6 S6 N c->setPartent(p);. ]' S3 J9 L! A delete c; //only delete one5 E6 k0 |) Z8 d3 a' f" s% U }% K2 U1 ^0 q: }+ Y& | return 0;( i" T+ x+ h8 d T8 K } 循环引用内存泄露的问题 #include <iostream>#include <memory> 6 l0 r- T" ?( @! G2 Y9 [ class Child;; i4 ^$ m" x' J# S! g# n j# S$ Y class Parent; 0 s' e D' D6 A: W class Parent { private: std::shared_ptr<Child> ChildPtr; public:( N6 m. t, r/ K/ y- X; m void setChild(std::shared_ptr<Child> child) {! Q) W; n; r5 s. Q8 v$ F this->ChildPtr = child;5 e' f. Y7 T+ k' q } void doSomething() { if (this->ChildPtr.use_count()) { . z+ u/ M. n; X) l } } . Z f7 g, e+ b0 y' L# s, q ~Parent() {1 [9 y2 e& d) E2 V } };! d) \9 G! Q9 X7 A8 S 7 P! A' Y- R9 j. |1 ]' i class Child {8 h- H& d3 }. E6 h% \ private:; p# @5 O6 R V8 s: v2 o std::shared_ptr<Parent> ParentPtr; public:2 ?4 h0 l: Y0 b/ g4 Z* w void setPartent(std::shared_ptr<Parent> parent) { this->ParentPtr = parent;& e Y5 i1 j' r0 p) y, s } void doSomething() {$ k: u. k0 \ v& x. h1 R if (this->ParentPtr.use_count()) {7 J4 p/ C, d3 p1 f6 d ; U1 Y4 O* G* \ } } ~Child() {" |3 a7 H9 i% \ m" \- i3 y" b1 M }/ K `3 @4 v$ a' ^% W/ A+ e8 W6 T }; int main() { std::weak_ptr<Parent> wpp; N' \" `$ `) m. \; l j std::weak_ptr<Child> wpc; { std::shared_ptr<Parent> p(new Parent);( p- I% u. @: D0 L4 K+ X std::shared_ptr<Child> c(new Child);1 f* K' y3 T m$ S p->setChild(c); c->setPartent(p);' ?3 @ ^* g# }( {- ]* n7 ~% B; r. B6 K wpp = p;8 S L5 `" x+ |. A wpc = c; std::cout << p.use_count() << std::endl; // 2 std::cout << c.use_count() << std::endl; // 24 P# Z. n# ]0 i* b) d! y }# F! e" I5 F: j std::cout << wpp.use_count() << std::endl; // 1 std::cout << wpc.use_count() << std::endl; // 11 U o* a' l8 C return 0; } d! p4 }7 [ J/ z 正确的做法 #include <iostream>* U Q [: |7 ]* r& h+ `- ]9 O#include <memory> class Child; class Parent;' J# H& M# t+ D ! U9 Z# d+ D8 ^* w+ z/ @ class Parent { private:0 M2 C* H7 m6 G# E3 w/ c! S% ?' \ //std::shared_ptr<Child> ChildPtr;5 n O; x9 E- D: j. t std::weak_ptr<Child> ChildPtr; public:/ ~, e/ O& U. X6 V void setChild(std::shared_ptr<Child> child) {/ R; s- i! ^+ @6 a6 _& K this->ChildPtr = child;. r- o6 T+ ~2 x9 N) E! f } void doSomething() {& C6 n, s! U2 A3 e1 A. U( [ //new shared_ptr( N' U& \" i8 }, X if (this->ChildPtr.lock()) {9 E y/ L5 E# r3 w* ` 2 L A, C5 s$ X) Q6 T }& f" t* E* Q% y# U) N9 K4 y }, F: L! S q9 W: | 0 m1 v" e" O2 Z" b! Z4 D3 } ~Parent() { } }; : O6 D9 y& O( n5 C1 J: ^ class Child { private:; {( H8 r' H) T8 j8 T- l std::shared_ptr<Parent> ParentPtr; public:: |' J o' }! e3 v6 G0 W3 C void setPartent(std::shared_ptr<Parent> parent) { this->ParentPtr = parent; }& Q; T' ^7 I' {2 W6 ` void doSomething() {& V ]; O$ _. s' z! v8 w' x5 j/ ?, t if (this->ParentPtr.use_count()) { & `1 _3 H6 u5 V1 y }1 p, Q+ c, d4 I [ |- U }4 u8 p! Y1 C# z ~Child() {' V! ]7 }0 z: Q, k } };! {* ~& i- q: A5 B- k4 C7 y6 d int main() {+ V" P. ^; X7 L$ x std::weak_ptr<Parent> wpp;% w8 t# W9 t3 \- J1 m- n. y std::weak_ptr<Child> wpc; {" X3 `2 Z2 A) u/ I, i5 ` std::shared_ptr<Parent> p(new Parent);7 ?. G* s& x2 l# P$ r+ C std::shared_ptr<Child> c(new Child); p->setChild(c); c->setPartent(p);+ Z6 ^8 m# l: k: t: J wpp = p;* N7 R/ S* l7 G' b$ j/ e wpc = c; std::cout << p.use_count() << std::endl; // 2% H5 q# P4 ^- o- d b: R std::cout << c.use_count() << std::endl; // 1+ M- V p6 C2 w# E9 P! y } std::cout << wpp.use_count() << std::endl; // 0 std::cout << wpc.use_count() << std::endl; // 0% j7 u. b3 s1 M7 Z5 T' m return 0;9 J: p3 Y# O" r! L/ L3 P }+ m; P" ~: `- K+ @" r- r1 x6 Q0 b 3、智能指针的设计和实现 下面是一个简单智能指针的demo。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。智能指针就是模拟指针动作的类。所有的智能指针都会重载 -> 和 * 操作符。智能指针还有许多其他功能,比较有用的是自动销毁。这主要是利用栈对象的有限作用域以及临时对象(有限作用域实现)析构函数释放内存。 1 #include <iostream>. ^2 ]; H( w/ U( b5 u5 @! ^7 G2 #include <memory> 3 4 template<typename T>. S6 V/ u7 Y3 h( |* t7 J7 B- f 5 class SmartPointer { 6 private: 7 T* _ptr;+ J7 n7 w+ ~. F. N 8 size_t* _count;* }% O" @8 Q5 H1 N8 s8 R0 o 9 public:$ q" p& n" o/ ~9 p7 f 10 SmartPointer(T* ptr = nullptr) : 11 _ptr(ptr) {' m6 O* ^. ^# W. P: M' q' E 12 if (_ptr) {) P+ U# P) f, z' O5 s" o# n% y7 U$ ] 13 _count = new size_t(1); |6 r' N w* N& ^; C+ A 14 } else {- |" h0 e+ o2 G) Y. y% P 15 _count = new size_t(0);/ l# x8 K7 Y* s3 }' A 16 } 17 }9 X g& b! h# p0 Z 18 19 SmartPointer(const SmartPointer& ptr) { 20 if (this != &ptr) { 21 this->_ptr = ptr._ptr; 22 this->_count = ptr._count;9 m. t3 n2 A- h, c/ d' } 23 (*this->_count)++;( w' W0 D2 L4 ?2 Z4 y: V/ \! W 24 }1 _1 }% |4 u5 J6 R4 ^6 D 25 }" {! ~; j& B1 ? 26 27 SmartPointer& operator=(const SmartPointer& ptr) {5 E9 n4 X+ m; m6 X 28 if (this->_ptr == ptr._ptr) {- N; Y# ^' ~1 t7 c3 G 29 return *this;( P [9 S |; C! | 30 }" N) h; L4 {8 H% N 31 9 Y' R9 N& C1 o& I% d1 f/ w- q( k 32 if (this->_ptr) {* o, |4 X9 K: j2 p% \ 33 (*this->_count)--; 34 if (this->_count == 0) { 35 delete this->_ptr; 36 delete this->_count; 37 }& E( j, |, U, W$ U" T* W$ e0 ^' J 38 } 39 40 this->_ptr = ptr._ptr; 41 this->_count = ptr._count;5 v9 Q$ w0 M, p7 l 42 (*this->_count)++; 43 return *this;& ?+ Z7 E& X5 l B0 M 44 }3 m4 N# [1 u- B! A' I# A 45 6 R. Z. a% O2 h/ U 46 T& operator*() { 47 assert(this->_ptr == nullptr);* O' f. v) B! O* O 48 return *(this->_ptr); 49 , i9 o7 \" u, F$ a5 \9 S 50 }7 a+ g9 U- b( V, E" y 51 52 T* operator->() {+ @% ~6 i1 Y7 O; P1 F6 H 53 assert(this->_ptr == nullptr);6 p$ m9 h x5 v& ^) A, S# {& g( Z 54 return this->_ptr; 55 } 56 + {, A; v/ F) h8 q4 } 57 ~SmartPointer() { 58 (*this->_count)--; 59 if (*this->_count == 0) {/ x% d: U, m, k) d `% ^; v 60 delete this->_ptr; 61 delete this->_count;! s4 D$ O# g$ g! z8 M0 w, G 62 } 63 } 64 65 size_t use_count(){# e" F- g! C/ d3 P 66 return *this->_count;' v% W! M) g* [' {$ Q! N/ } 67 } 68 }; 69 1 a; U7 ~' ^) h- [ 70 int main() { 71 {: y' @9 m0 R3 o( y6 J7 \: W/ p 72 SmartPointer<int> sp(new int(10));# ^) Z3 u. B6 X9 o6 l 73 SmartPointer<int> sp2(sp); 74 SmartPointer<int> sp3(new int(20));/ }! b% h. x2 k- D" z" G 75 sp2 = sp3;/ s# x* g( _6 b% e 76 std::cout << sp.use_count() << std::endl; 77 std::cout << sp3.use_count() << std::endl; 78 } 79 //delete operator 80 } |
| 顶一下 |
微信公众号
手机版