Smart Pointer是C++中的一個(gè)大題目,要說清楚他的所有好處很需要費(fèi)點(diǎn)力氣。我就一個(gè)功能一個(gè)功能的說。有我理解不透的地方希望大家指點(diǎn)。
1.copy-to-write
當(dāng)生成一個(gè)C++ object的時(shí)候如果這個(gè)class很大,這個(gè)object會(huì)占用很多空間。那么每生成一個(gè)就占用一片空間,這樣會(huì)占用很多系統(tǒng)資源。同時(shí)降低效率。一個(gè)解決方法就是對(duì)用拷貝構(gòu)造函數(shù)生成的object,讓他不存儲(chǔ)數(shù)據(jù),而只存儲(chǔ)一個(gè)指向原來object數(shù)據(jù)的指針。 這樣空間就節(jié)省了很多。但問題在于這樣兩個(gè)object完全聯(lián)結(jié)在了一起。如果修改了其中一個(gè),另一個(gè)也跟著變了。所以這種方法不可取。這里講的 copy-to-write技術(shù)就是解決這類問題的方法。當(dāng)通過引用一個(gè)已有object去拷貝構(gòu)造新object時(shí),新object只有一個(gè)指向已有 object的指針。
這兩個(gè)object共享數(shù)據(jù)。直到其中一個(gè)需要修改數(shù)據(jù)的時(shí)候,再把這兩塊數(shù)據(jù)分離。這里舉一個(gè)最簡(jiǎn)化的例子。假設(shè)一個(gè)class叫
CLargeObject,里面存有很多數(shù)據(jù)。我們用一個(gè)inner
class來把所有數(shù)據(jù)放在一起,叫CData。CData里面存有大量數(shù)據(jù),例如一個(gè)數(shù)據(jù)庫。這里用最簡(jiǎn)單的模型來表示,假設(shè)只有一個(gè)整數(shù)int m_nVal; CData里面需要包含另一個(gè)變量。叫作索引數(shù)目(reference count)。它記錄了指向這個(gè)CData object的來自CLargetObject類的指針各數(shù)。也就是說,總共有多少CLargeObject的object正在引用著當(dāng)前的CData object。
class CLargeObject
{
private:
struct CData
{
private:
int m_nVal;
int m_nReferenceCount;
}
};
對(duì)于每個(gè)CLargeObject的object,我們用一個(gè)CData類的指針來指向其數(shù)據(jù)。
CData *m_pData;
CLargeObject至少有兩個(gè)構(gòu)造函數(shù)。第一個(gè)是標(biāo)準(zhǔn)的構(gòu)造函數(shù),初始化其數(shù)據(jù)。這時(shí)數(shù)據(jù)是唯一的,所以必須新生成一個(gè)CData的object來存儲(chǔ)數(shù)據(jù)。
CLargeObject::CLargeObject(int nVal)
{
m_pData = new Data(nVal);
}
而對(duì)于CData類的構(gòu)造函數(shù)而言,初始化他的CLargeObject是第一個(gè)指向他的,這一時(shí)刻索引數(shù)目m_nReferenceCount是1。
CLargeObject::Data::Data(int nVal) : m_nVal(nVal), m_nReferenceCount(1) {}
CLargeObject的第二個(gè)構(gòu)造函數(shù)是拷貝構(gòu)造(copy constructor)。這樣生成的object不需要有新的數(shù)據(jù),和已有的object共享數(shù)據(jù)就可以了。這是索引數(shù)目需要加1。表示又有一個(gè)object指向當(dāng)前的CData了。
CLargeObject::CLargeObject(const CLargeObject &ob) // copy constructor
{
ob.m_pData->m_nReferenceCount++;
m_pData = ob.m_pData;
}
這樣CLargeObject就構(gòu)造好了,使用了可能的最少的內(nèi)存。下面看看他的析夠函數(shù)(destructor)。當(dāng)一個(gè)object被delete的時(shí)候,它的數(shù)據(jù)不一定無效,如果別的object還在引用著這個(gè)數(shù)據(jù),數(shù)據(jù)需要留下來。當(dāng)然,數(shù)據(jù)的索引數(shù)目無論如何都要減1。
CLargeObject::~CLargeObject()
{
if (--m_pData->m_nReferenceCount == 0)
delete m_pData;
}
下面看一看賦值操作。先說用已有的CLargeObject賦值給這個(gè)CLargeObject。這時(shí)當(dāng)前CLargeObject里面的數(shù)據(jù)要指向已有的這個(gè)object,就搞定了。
CLargeObject& CLargeObject::operator = (const CLargeObject& ob) // copy assignment
{
ob.m_pData->m_nReferenceCount++;
if (--m_pData->m_nReferenceCount == 0)
delete m_pData;
m_pData = ob.m_pData;
return *this;
}
再來看看如何對(duì)CLargeObject里面的數(shù)據(jù)進(jìn)行真正的修改。這樣就一定需要對(duì)當(dāng)前的object獨(dú)立操作了,否則就影響到了其它指向同一塊數(shù)據(jù)的CLargeObject。這樣CData類需要一個(gè)新的函數(shù),生成只用于當(dāng)前CLargetObject的數(shù)據(jù)。如果當(dāng)前的引用數(shù)目是1,那么當(dāng)然這個(gè)CData就是只用于這個(gè)CLargeObject的了。否則就重新new一個(gè)CData返回。
Data* CLargeObject::CData::get_own_copy() // clone if necessary
{
if (m_nReferenceCount==1)
return this;
m_nReferenceCount--;
return new Data(m_nVal);
}
CLargeObject修改前用這個(gè)函數(shù)得到唯一的object,然后對(duì)它賦值。
void CLargeObject::SetVal(int nNewVal)
{
m_pData = m_pData->get_own_copy();
m_pData->m_nVal = nNewVal;
}
對(duì)于所有可能改變CData值的操作,都需要用這種方法。
下面是只讀函數(shù),簡(jiǎn)單。直接返回值,什么特殊的都不用作。
int CLargeObject::GetVal() const
{
return m_pData->m_nVal;
}
這樣copy-to-write技術(shù)就實(shí)現(xiàn)了。下面把完整的程序?qū)懸幌拢?br>class CLargeObject
{
public:
CLargeObject(int nVal);
CLargeObject(const CLargeObject &ob);
~CLargeObject();
CLargeObject& operator = (const CLargeObject& ob);
void SetVal(int nNewVal);
int GetVal() const;
private:
struct Data
{
public:
Data(int nVal) : m_nVal(nVal), m_nReferenceCount(1) {}
private:
friend class CLargeObject;
Data* get_own_copy() // clone if necessary
{
if (m_nReferenceCount==1)
return this;
m_nReferenceCount--;
return new Data(m_nVal);
}
// control variables.
int m_nReferenceCount;
// actual data portion
int m_nVal;
};
Data *m_pData;
};
CLargeObject::CLargeObject(int nVal)
{
m_pData = new Data(nVal);
}
CLargeObject::CLargeObject(const CLargeObject &ob) // copy constructor
{
ob.m_pData->m_nReferenceCount++;
m_pData = ob.m_pData;
}
CLargeObject::~CLargeObject()
{
if (--m_pData->m_nReferenceCount == 0)
delete m_pData;
}
CLargeObject& CLargeObject::operator = (const CLargeObject& ob) // copy assignment
{
ob.m_pData->m_nReferenceCount++;
if (--m_pData->m_nReferenceCount == 0)
delete m_pData;
m_pData = ob.m_pData;
return *this;
}
void CLargeObject::SetVal(int nNewVal)
{
m_pData = m_pData->get_own_copy();
m_pData->m_nVal = nNewVal;
}
int CLargeObject::GetVal() const
{
return m_pData->m_nVal;
}
很多存儲(chǔ)數(shù)據(jù)的系統(tǒng)class,如string,CString等都有這種設(shè)計(jì)。所以記住這個(gè)應(yīng)用是很有必要的。