c++11中智能指针的原理,使用,实现
文章目錄
- 理解智能指針的原理
- 智能指針的使用
- shared_ptr的使用
- unique_ptr的使用
- weak_ptr的使用
- 循環(huán)引用
- 指針指針的設(shè)計和實現(xiàn):
理解智能指針的原理
c++程序設(shè)計中使用堆內(nèi)存是非常頻繁的操作,堆內(nèi)存的申請和釋放都由程序員自己管理。程序員自己管理堆內(nèi)存可以提高了程序的效率,但是整體來說堆內(nèi)存的管理是非常麻煩的,c++11中引入了智能指針的概念,方便管理堆內(nèi)存。使用普通指針,容易造成堆內(nèi)存泄露(忘記釋放),二次釋放,程序發(fā)生異常時內(nèi)存泄漏等問題等,使用智能指針能更好的管理堆內(nèi)存。
理解智能指針需要從下面三個層次:
智能指針的使用
指針指針在c++11版本之后提供,包含在< memory >中,shared_ptr,unique_ptr,weak_ptr
shared_ptr的使用
shared_ptr多個指針指向相同的對象,shared_ptr使用引用計數(shù),每一個shared_ptr的拷貝都指向相同的內(nèi)存。每使用一次他,內(nèi)部的引用計數(shù)加1,每析構(gòu)一次,內(nèi)部的引用計數(shù)減1,減為0時,自動刪除所指向的堆內(nèi)存時。shared_ptr內(nèi)部的引用計數(shù)是線程安全的,但是對象的讀取需要加鎖。
- 初始化。智能指針是個模板類,可以指定類型,傳入指針通過構(gòu)造函數(shù) 初始化,也可以使用make_shared 函數(shù)初始化,不能將指針賦值給一個智能指針,一個是類,一個是指針。例如std::shared_ptr< int >p4=new int (1);寫法是錯誤的
- 拷貝和賦值。拷貝使得對象的引用計數(shù)增加1,賦值使得原對象引用計數(shù)減1,當(dāng)計數(shù)為0時,自動釋放內(nèi)存。后來指向的對象引用計數(shù)加1,指向后來的對象
- get函數(shù)獲取原始指針
- 注意不要用一個原始指針初始化多個shared_ptr,否則會造成二次釋放同一個內(nèi)存
- 注意避免循環(huán)引用,shared_ptr最大的陷阱就是循環(huán)引用,循環(huán),循環(huán)引用會導(dǎo)致堆內(nèi)存無法正確釋放,導(dǎo)致內(nèi)存泄漏。循環(huán)引用在weak_ptr中介紹
unique_ptr的使用
unique_ptr"唯一"擁有其所指對象,同一時刻智能有一個unique_ptr指向給定對象(通過禁止拷貝語義,只有移動語義來實現(xiàn))。相比于原始指針unique_ptr用于其RAII的特性,使得在出現(xiàn)異常的情況下,動態(tài)資源能得到釋放。unique_ptr指針本身的生命周期:從unique_ptr指針創(chuàng)建時開始,直到離開作用域。離開作用域時,若其指向?qū)ο?#xff0c;則將其所指對象銷毀(默認(rèn)使用delete操作符,用戶可指定其它操作)。unique_ptr指針與其所指對象的關(guān)系:在智能指針生命周期內(nèi),可以改變智能指針?biāo)傅膶ο?#xff0c;如創(chuàng)建智能指針時通過構(gòu)造函數(shù)指定,通過reset方法重新指定,通過release方法釋放所有權(quán),通過移動語義轉(zhuǎn)移所有權(quán)。
#include <iostream> #include <memory> int main() {{std::unique_ptr<int> uptr(new int(10)); //綁定動態(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)存釋放}weak_ptr的使用
weak_ptr是為了配合shared_ptr而引入的一種只能指針,因為它不具有普通指針的行為,沒有重載operator * 和 ->,它的最大作用在于協(xié)助shared_ptr工作,像旁觀者那樣觀測資源的使用情況。weak_ptr可以從一個shared_ptr或者另一個weak_ptr對象構(gòu)造,獲得資源的觀測權(quán)。但weak_ptr沒有共享資源,它的構(gòu)造不會引起指針引用計數(shù)的增加。使用weak_ptr的成員函數(shù)use_count()可以觀測資源的引用計數(shù),另一個成員函數(shù)的expired()的功能等價于use_count()==0,但更快,表示被觀測的資源(也就是shared_ptr的管理的資源)已經(jīng)不復(fù)存在。weak_ptr可以使用一個非常重要的成員函數(shù)lock()從被觀測的shared_ptr獲得一個可用的shared_ptr對象,從而操作資源。但當(dāng)expired()==true的時候,lock()函數(shù)將返回一個存儲空指針的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 }循環(huán)引用
考慮一個簡單的對象建模——家長與子女,a Parent has a child ,a child knowshis/her parent。在java里面很好寫,不用擔(dān)心內(nèi)存泄漏,也不用擔(dān)心空懸指針,只要正確初始化myChild和myParent,那么java程序員就不用擔(dān)心出現(xiàn)訪問錯誤。一個handle是否有效,只需要判斷其是否nonnull。
public class Parent { private Child myChild; } public class Child { private Parent myParent; }在c++里邊就是為資源管理費一番腦筋,如果使用原始指針作為成員,Child和Parent由誰釋放?那么如何保證指針的有效性?如何防止出現(xiàn)空懸指針?這些問題是c++面向?qū)ο笞兂傻穆闊┑膯栴},現(xiàn)在可以借助smart pointer 把語義對象(pointer)轉(zhuǎn)變?yōu)橹?#xff08;value)語義,shared_ptr輕松解決聲明周期的問題,不用擔(dān)心空懸指針。但是這個模型存在循環(huán)引用的問題,注意其中一個指針應(yīng)該為weak_ptr。
原始指針的做法,容易出錯:
#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; // 2std::cout << c.use_count() << std::endl; // 2}std::cout << wpp.use_count() << std::endl; // 1std::cout << wpc.use_count() << std::endl; // 1return 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_ptrif (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; // 2std::cout << c.use_count() << std::endl; // 1}std::cout << wpp.use_count() << std::endl; // 0std::cout << wpc.use_count() << std::endl; // 0return 0; }指針指針的設(shè)計和實現(xiàn):
下面是一個簡單智能指針的demo。智能指針將一個累計數(shù)器和類指向的對象相關(guān)聯(lián),引用計數(shù)跟蹤該類有多少個對象共享同一個指針。每次創(chuàng)建類的新對象時,初始化指針并將引用計數(shù)置位1;當(dāng)對象作為另一個對象的副本而創(chuàng)建時,拷貝構(gòu)造函數(shù)指針并增加與之相應(yīng)的引用計數(shù);每一個對象進(jìn)行賦值時,賦值操作符減少左操作數(shù)所指對象的引用計數(shù)(如果引用計數(shù)為減至0,則刪除對象),并增加右操作數(shù)所指對象的引用計數(shù);調(diào)用析構(gòu)函數(shù)時,構(gòu)造函數(shù)減少引用計數(shù)(如果引用計數(shù)減至0,則刪除基礎(chǔ)對象)。智能指針就是模擬指針動作的類。所有的智能指針都會重載->和*操作符,智能指針還有許多其它功能,比較有效的是自動銷毀。這主要是利用棧對象的有限作用域以及臨時對象(有限作用域?qū)崿F(xiàn))析構(gòu)函數(shù)釋放內(nèi)存
#include <iostream>#include <memory>template<typename T>class SmartPointer {private:T* _ptr;size_t* _count;public:SmartPointer(T* ptr = nullptr) :_ptr(ptr) {if (_ptr) {_count = new size_t(1);} else {_count = new size_t(0);}}SmartPointer(const SmartPointer& ptr) {if (this != &ptr) {this->_ptr = ptr._ptr;this->_count = ptr._count;(*this->_count)++;}}SmartPointer& operator=(const SmartPointer& ptr) {if (this->_ptr == ptr._ptr) {return *this;}if (this->_ptr) {(*this->_count)--;if (this->_count == 0) {delete this->_ptr;delete this->_count;}}this->_ptr = ptr._ptr;this->_count = ptr._count;(*this->_count)++;return *this;}T& operator*() {assert(this->_ptr == nullptr);return *(this->_ptr);}T* operator->() {assert(this->_ptr == nullptr);return this->_ptr;}~SmartPointer() {(*this->_count)--;if (*this->_count == 0) {delete this->_ptr;delete this->_count;}} size_t use_count(){return *this->_count;}};int main() {{SmartPointer<int> sp(new int(10));SmartPointer<int> sp2(sp);SmartPointer<int> sp3(new int(20));sp2 = sp3;std::cout << sp.use_count() << std::endl;std::cout << sp3.use_count() << std::endl;}//delete operator}總結(jié)
以上是生活随笔為你收集整理的c++11中智能指针的原理,使用,实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 汇编层探索与讨论c++引用
- 下一篇: 题目2:隐式图的搜索问题(A*算法解决八