目錄
1.智能指針的作用C 程序設(shè)計(jì)中使用堆內(nèi)存是非常頻繁的操作,堆內(nèi)存的申請和釋放都由程序員自己管理。程序員自己管理堆內(nèi)存可以提高了程序的效率,但是整體來說堆內(nèi)存的管理是麻煩的,C 11中引入了智能指針的概念,方便管理堆內(nèi)存。使用普通指針,容易造成堆內(nèi)存泄露(忘記釋放),二次釋放,程序發(fā)生異常時(shí)內(nèi)存泄露等問題等,使用智能指針能更好的管理堆內(nèi)存。 理解智能指針需要從下面三個(gè)層次:
Animal a = new Animal(); Animal b = a; 你當(dāng)然知道,這里其實(shí)只生成了一個(gè)對象,a和b僅僅是把持對象的引用而已。但在C 中不是這樣, Animal a; Animal b = a; 這里卻是就是生成了兩個(gè)對象。 關(guān)于值語言參考這篇文章http://www.cnblogs.com/Solstice/archive/2011/08/16/2141515.html 2.智能指針的使用智能指針在C 11版本之后提供,包含在頭文件<memory>中,shared_ptr、unique_ptr、weak_ptr 2.1 shared_ptr的使用shared_ptr多個(gè)指針指向相同的對象。shared_ptr使用引用計(jì)數(shù),每一個(gè)shared_ptr的拷貝都指向相同的內(nèi)存。每使用他一次,內(nèi)部的引用計(jì)數(shù)加1,每析構(gòu)一次,內(nèi)部的引用計(jì)數(shù)減1,減為0時(shí),自動(dòng)刪除所指向的堆內(nèi)存。shared_ptr內(nèi)部的引用計(jì)數(shù)是線程安全的,但是對象的讀取需要加鎖。
#include <iostream> #include <memory> int main() { { int a = 10; std::shared_ptr<int> ptra = std::make_shared<int>(a); std::shared_ptr<int> ptra2(ptra); //copy std::cout << ptra.use_count() << std::endl; int b = 20; int *pb = &a; //std::shared_ptr<int> ptrb = pb; //error std::shared_ptr<int> ptrb = std::make_shared<int>(b); ptra2 = ptrb; //assign pb = ptrb.get(); //獲取原始指針 std::cout << ptra.use_count() << std::endl; std::cout << ptrb.use_count() << std::endl; } } 2.2 unique_ptr的使用unique_ptr“唯一”擁有其所指對象,同一時(shí)刻只能有一個(gè)unique_ptr指向給定對象(通過禁止拷貝語義、只有移動(dòng)語義來實(shí)現(xiàn))。相比與原始指針unique_ptr用于其RAII的特性,使得在出現(xiàn)異常的情況下,動(dòng)態(tài)資源能得到釋放。unique_ptr指針本身的生命周期:從unique_ptr指針創(chuàng)建時(shí)開始,直到離開作用域。離開作用域時(shí),若其指向?qū)ο?,則將其所指對象銷毀(默認(rèn)使用delete操作符,用戶可指定其他操作)。unique_ptr指針與其所指對象的關(guān)系:在智能指針生命周期內(nèi),可以改變智能指針?biāo)笇ο?,如?chuàng)建智能指針時(shí)通過構(gòu)造函數(shù)指定、通過reset方法重新指定、通過release方法釋放所有權(quán)、通過移動(dòng)語義轉(zhuǎn)移所有權(quán)。 #include <iostream> #include <memory> int main() { { std::unique_ptr<int> uptr(new int(10)); //綁定動(dòng)態(tài)對象 //std::unique_ptr<int> uptr2 = uptr; //不能賦值 //std::unique_ptr<int> uptr2(uptr); //不能拷貝 std::unique_ptr<int> uptr2 = std::move(uptr); //轉(zhuǎn)換所有權(quán) uptr2.release(); //釋放所有權(quán) } //超過uptr的作用域,內(nèi)存釋放 } 2.3 weak_ptr的使用weak_ptr是為了配合shared_ptr而引入的一種智能指針,因?yàn)樗痪哂衅胀ㄖ羔樀男袨椋瑳]有重載operator*和->,它的最大作用在于協(xié)助shared_ptr工作,像旁觀者那樣觀測資源的使用情況。weak_ptr可以從一個(gè)shared_ptr或者另一個(gè)weak_ptr對象構(gòu)造,獲得資源的觀測權(quán)。但weak_ptr沒有共享資源,它的構(gòu)造不會引起指針引用計(jì)數(shù)的增加。使用weak_ptr的成員函數(shù)use_count()可以觀測資源的引用計(jì)數(shù),另一個(gè)成員函數(shù)expired()的功能等價(jià)于use_count()==0,但更快,表示被觀測的資源(也就是shared_ptr的管理的資源)已經(jīng)不復(fù)存在。weak_ptr可以使用一個(gè)非常重要的成員函數(shù)lock()從被觀測的shared_ptr獲得一個(gè)可用的shared_ptr對象, 從而操作資源。但當(dāng)expired()==true的時(shí)候,lock()函數(shù)將返回一個(gè)存儲空指針的shared_ptr。 #include <iostream> #include <memory> int main() { { 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; if(!wp.expired()){ std::shared_ptr<int> sh_ptr2 = wp.lock(); //get another shared_ptr *sh_ptr = 100; std::cout << wp.use_count() << std::endl; } } //delete memory } 2.4 循環(huán)引用考慮一個(gè)簡單的對象建?!议L與子女:a Parent has a Child, a Child knowshis/her Parent。在Java 里邊很好寫,不用擔(dān)心內(nèi)存泄漏,也不用擔(dān)心空懸指針,只要正確初始化myChild 和myParent,那么Java 程序員就不用擔(dān)心出現(xiàn)訪問錯(cuò)誤。一個(gè)handle 是否有效,只需要判斷其是否non null。 public class Parent 原始指針的做法,容易出錯(cuò) #include <iostream> #include <memory> class Child; class Parent; class Parent { private: Child* myChild; public: void setChild(Child* ch) { this->myChild = ch; } void doSomething() { if (this->myChild) { } } ~Parent() { delete myChild; } }; class Child { private: Parent* myParent; public: void setPartent(Parent* p) { this->myParent = p; } void doSomething() { if (this->myParent) { } } ~Child() { delete myParent; } }; int main() { { Parent* p = new Parent; Child* c = new Child; p->setChild(c); c->setPartent(p); delete c; //only delete one } return 0; } 循環(huán)引用內(nèi)存泄露的問題 #include <iostream> #include <memory> class Child; class Parent; class Parent { private: std::shared_ptr<Child> ChildPtr; public: void setChild(std::shared_ptr<Child> child) { this->ChildPtr = child; } void doSomething() { if (this->ChildPtr.use_count()) { } } ~Parent() { } }; class Child { private: std::shared_ptr<Parent> ParentPtr; public: void setPartent(std::shared_ptr<Parent> parent) { this->ParentPtr = parent; } void doSomething() { if (this->ParentPtr.use_count()) { } } ~Child() { } }; int main() { std::weak_ptr<Parent> wpp; std::weak_ptr<Child> wpc; { std::shared_ptr<Parent> p(new Parent); std::shared_ptr<Child> c(new Child); p->setChild(c); c->setPartent(p); wpp = p; wpc = c; std::cout << p.use_count() << std::endl; // 2 std::cout << c.use_count() << std::endl; // 2 } std::cout << wpp.use_count() << std::endl; // 1 std::cout << wpc.use_count() << std::endl; // 1 return 0; } 正確的做法 #include <iostream> #include <memory> class Child; class Parent; class Parent { private: //std::shared_ptr<Child> ChildPtr; std::weak_ptr<Child> ChildPtr; public: void setChild(std::shared_ptr<Child> child) { this->ChildPtr = child; } void doSomething() { //new shared_ptr if (this->ChildPtr.lock()) { } } ~Parent() { } }; class Child { private: std::shared_ptr<Parent> ParentPtr; public: void setPartent(std::shared_ptr<Parent> parent) { this->ParentPtr = parent; } void doSomething() { if (this->ParentPtr.use_count()) { } } ~Child() { } }; int main() { std::weak_ptr<Parent> wpp; std::weak_ptr<Child> wpc; { std::shared_ptr<Parent> p(new Parent); std::shared_ptr<Child> c(new Child); p->setChild(c); c->setPartent(p); wpp = p; wpc = c; std::cout << p.use_count() << std::endl; // 2 std::cout << c.use_count() << std::endl; // 1 } std::cout << wpp.use_count() << std::endl; // 0 std::cout << wpc.use_count() << std::endl; // 0 return 0; } 3.智能指針的設(shè)計(jì)和實(shí)現(xiàn)下面是一個(gè)簡單智能指針的demo。智能指針類將一個(gè)計(jì)數(shù)器與類指向的對象相關(guān)聯(lián),引用計(jì)數(shù)跟蹤該類有多少個(gè)對象共享同一指針。每次創(chuàng)建類的新對象時(shí),初始化指針并將引用計(jì)數(shù)置為1;當(dāng)對象作為另一對象的副本而創(chuàng)建時(shí),拷貝構(gòu)造函數(shù)拷貝指針并增加與之相應(yīng)的引用計(jì)數(shù);對一個(gè)對象進(jìn)行賦值時(shí),賦值操作符減少左操作數(shù)所指對象的引用計(jì)數(shù)(如果引用計(jì)數(shù)為減至0,則刪除對象),并增加右操作數(shù)所指對象的引用計(jì)數(shù);調(diào)用析構(gòu)函數(shù)時(shí),構(gòu)造函數(shù)減少引用計(jì)數(shù)(如果引用計(jì)數(shù)減至0,則刪除基礎(chǔ)對象)。智能指針就是模擬指針動(dòng)作的類。所有的智能指針都會重載 -> 和 * 操作符。智能指針還有許多其他功能,比較有用的是自動(dòng)銷毀。這主要是利用棧對象的有限作用域以及臨時(shí)對象(有限作用域?qū)崿F(xiàn))析構(gòu)函數(shù)釋放內(nèi)存。 1 #include <iostream> 2 #include <memory> 3 4 template<typename T> 5 class SmartPointer { 6 private: 7 T* _ptr; 8 size_t* _count; 9 public: 10 SmartPointer(T* ptr = nullptr) : 11 _ptr(ptr) { 12 if (_ptr) { 13 _count = new size_t(1); 14 } else { 15 _count = new size_t(0); 16 } 17 } 18 19 SmartPointer(const SmartPointer& ptr) { 20 if (this != &ptr) { 21 this->_ptr = ptr._ptr; 22 this->_count = ptr._count; 23 (*this->_count) ; 24 } 25 } 26 27 SmartPointer& operator=(const SmartPointer& ptr) { 28 if (this->_ptr == ptr._ptr) { 29 return *this; 30 } 31 32 if (this->_ptr) { 33 (*this->_count)--; 34 if (this->_count == 0) { 35 delete this->_ptr; 36 delete this->_count; 37 } 38 } 39 40 this->_ptr = ptr._ptr; 41 this->_count = ptr._count; 42 (*this->_count) ; 43 return *this; 44 } 45 46 T& operator*() { 47 assert(this->_ptr == nullptr); 48 return *(this->_ptr); 49 50 } 51 52 T* operator->() { 53 assert(this->_ptr == nullptr); 54 return this->_ptr; 55 } 56 57 ~SmartPointer() { 58 (*this->_count)--; 59 if (*this->_count == 0) { 60 delete this->_ptr; 61 delete this->_count; 62 } 63 } 64 65 size_t use_count(){ 66 return *this->_count; 67 } 68 }; 69 70 int main() { 71 { 72 SmartPointer<int> sp(new int(10)); 73 SmartPointer<int> sp2(sp); 74 SmartPointer<int> sp3(new int(20)); 75 sp2 = sp3; 76 std::cout << sp.use_count() << std::endl; 77 std::cout << sp3.use_count() << std::endl; 78 } 79 //delete operator 80 } 參考:
|
|