C++ 智能指針深刻解析。本站提示廣大學習愛好者:(C++ 智能指針深刻解析)文章只能為提供參考,不一定能成為您想要的結果。以下是C++ 智能指針深刻解析正文
1. 為何須要智能指針?
簡略的說,智能指針是為了完成相似於Java中的渣滓收受接管機制。Java的渣滓收受接管機制使法式員從復雜的內存治理義務中完全的擺脫出來,在請求應用一塊內存區域以後,無需去存眷應當什麼時候何地釋放內存,Java將會主動贊助收受接管。然則出於效力和其他緣由(能夠C++設計者不屑於這類傻瓜氏的編程方法),C++自己並沒有如許的功效,其復雜且易失足的內存治理也一向為寬大法式員所诟病。
更進一步地說,智能指針的湧現是為了知足治理類中指針成員的須要。包括指針成員的類須要特殊留意復制掌握和賦值操作,緣由是復制指針時只復制指針中的地址,而不會復制指針指向的對象。當類的實例在析構的時刻,能夠會招致垂懸指針成績。
治理類中指針成員的辦法普通有兩種方法:一種是采取值型類,這類類是給指針成員供給值語義(value semantics),當復制該值型對象時,會獲得一個分歧的新正本。這類方法典范的運用是string類。別的一種方法就是智能指針,完成這類方法的指針所指向的對象是同享的。
2. 智能指針的完成概述
智能指針(smart pointer)的一種通用完成技巧是應用援用計數(reference count)。智能指針類將一個計數器與類指向的對象相干聯,援用計數跟蹤該類有若干個對象同享統一指針。
每次創立類的新對象時,初始化指針並將援用計數置為1;當對象作為另外一對象的正本而創立時,拷貝結構函數拷貝指針並增長與之響應的援用計數;對一個對象停止賦值時,賦值操作符削減左操作數所指對象的援用計數(假如援用計數為減至0,則刪除對象),並增長右操作數所指對象的援用計數;挪用析構函數時,結構函數削減援用計數(假如援用計數減至0,則刪除基本對象)。
完成智能指針有兩種經典戰略:一是引入幫助類,二是應用句柄類。
3. 完成方法1:引入幫助類
這類方法界說一個零丁的詳細類(RefPtr)來封裝指針和響應的援用計數。
class Point //基本對象類
{
public:
Point(int xVal = 0, int yVal = 0):x(xVal),y(yVal) { }
int getX() const { return x; }
int getY() const { return y; }
void setX(int xVal) { x = xVal; }
void setY(int yVal) { y = yVal; }
private:
int x,y;
};
class RefPtr //幫助類
{ //該類成員拜訪權限全體為private,由於不想讓用戶直接應用該類
friend class SmartPtr; //界說智能指針類為友元,由於智能指針類須要直接把持幫助類
RefPtr(Point *ptr):p(ptr), count(1) { }
~RefPtr() { delete p; }
int count; //援用計數
Point *p; //基本對象指針
};
class SmartPtr //智能指針類
{
public:
SmartPtr(Point *ptr):rp(new RefPtr(ptr)) { } //結構函數
SmartPtr(const SmartPtr &sp):rp(sp.rp) { ++rp->count; } //復制結構函數
SmartPtr& operator=(const SmartPtr& rhs) { //重載賦值操作符
++rhs.rp->count; //起首將右操作數援用計數加1,
if(--rp->count == 0) //然後將援用計數減1,可以應對自賦值
delete rp;
rp = rhs.rp;
return *this;
}
~SmartPtr() { //析構函數
if(--rp->count == 0) //當援用計數減為0時,刪除幫助類對象指針,從而刪除基本對象
delete rp;
}
private:
RefPtr *rp; //幫助類對象指針
};
int main()
{
Point *p1 = new Point(10, 8);
SmartPtr sp1(p1);
SmartPtr sp2(sp1);
Point *p2 = new Point(5, 5);
SmartPtr sp3(p2);
sp3 = sp1;
return 0;
}
應用該方法的內存構造圖以下:
4. 完成方法2:應用句柄類
為了不下面計劃中每一個應用指針的類本身去掌握援用計數,可以用一個類把指針封裝起來。封裝好後,這個類對象可以湧現在用戶類應用指針的任何處所,表示為一個指針的行動。我們可以像指針一樣應用它,而不消擔憂通俗成員指針所帶來的成績,我們把如許的類叫句柄類。在封裝句柄類時,須要請求一個靜態分派的援用計數空間,指針與援用計數離開存儲。完成示例以下:
class Point //基本對象類
{
public:
Point(int xVal = 0, int yVal = 0):x(xVal),y(yVal) { }
int getX() const { return x; }
int getY() const { return y; }
void setX(int xVal) { x = xVal; }
void setY(int yVal) { y = yVal; }
public:
virtual Point* clone() const { //虛函數,為了完成讓句柄類在不曉得對象切實其實切類型的情形下分派已知對象的新正本
return new Point(*this);
}
private:
int x,y;
};
class D3Point : public Point //派生類
{
public:
D3Point(int xVal, int yVal, int zVal):Point(xVal, yVal), z(zVal) { }
int getZ() const { return z; }
void setZ(int zVal) { z = zVal; }
public:
D3Point* clone() const { //虛函數,為了完成讓句柄類在不曉得對象切實其實切類型的情形下分派已知對象的新正本
return new D3Point(*this);
}
private:
int z;
};
class SmartPtr
{
public:
SmartPtr(Point *ptr = 0):p(ptr), count(new int(1)) { } //結構函數
SmartPtr(Point &point):p(point.clone()), count(new int(1)) { } //結構函數
SmartPtr(const SmartPtr &sp):p(sp.p), count(sp.count) { ++*count; } //復制結構函數
SmartPtr& operator=(const SmartPtr &sp) { //重載賦值操作符
++*sp.count; //起首將右操作數援用計數加1,
decr_use(); //然後將援用計數減1,可以應對自賦值
p = sp.p;
count = sp.count;
return *this;
}
~SmartPtr() { //析構函數
decr_use();
}
public: //普通情形下不會完成這兩個操作符,由於我們不願望用戶直接把持基本對象指針
const Point* operator->() const {
if(p) return p;
else throw logic_error("Unbound Point");
}
const Point& operator*() const {
if(p) return *p;
else throw logic_error("Unbound Point");
}
private:
void decr_use() {
if(--*count == 0)
{
delete p;
delete count;
}
}
private:
Point *p; //基本對象指針
int *count; //指向援用計數的指針
};
int main()
{
Point *p1 = new Point(10, 8);
SmartPtr sp1(p1);
SmartPtr sp2(sp1);
D3Point *p2 = new D3Point(5, 5, 0);
SmartPtr sp3(p2);
return 0;
}
應用該方法的內存構造圖以下: