基礎語法之六:類的複製控制

1、類定義

類成員:可以包括 數據、函數、類型別名等;

構造函數:初始化列表,在構造函數的形參列表後,由冒號起始,參數間以逗號隔開;

成員函數成員函數必須在類內聲明,定義是可選的,類內定義的成員函數默認爲inline,成員函數後   添加 const 表示本函數不改變類對象的數據成員,即this指針爲指向const對象的指針。

另外this永遠是一個 const 指針即只能指向類對象本身,不能人爲改變。

 

2、static類成員

2.1 static成員函數

static關鍵字只能出現在類定義內的成員聲明處,類似於explicit。 static成員函數沒有this參數所以不能聲明爲const

 

2.2 static數據成員

staic數據成員必須在類的定義體外部定義(only once),static數據成員不是通過構造函數初始化,而是應該在定義時進行初始化,原因是當類頭文件包含在多個源文件中時防止變量的重複定義;

 

特殊的整型const static成員:整型const static成員可以在類定義體中初始化,該成員仍然必須在類外部進行定義,不過不用使用初始化式。也就是說類內即使有初始化式也只是聲明和初始化,只有類外才是定義,纔會產生內存空間;

 

static成員不是類對象的組成部分

所以static成員類型可以是所屬類的類型本身,但是普通成員不可以,普通成員被限定爲類類型的指針或引用、或者其他變量類型。

栗子:

class Singleton

{
public:
        static Singleton* getInstance();

private:
        Singleton();
        //把複製構造函數和=操作符也設爲私有,防止被複制
        Singleton(const Singleton&);
        Singleton& operator=(const Singleton&);

   static Singleton* instance;

};

 

Singleton* Singleton::instance = new Singleton();
Singleton* Singleton::getInstance()

{
        return instance;
}

 

3、 複製控制

複製控制包括:複製構造函數、析構函數、賦值操作

如果沒有定義他們,編譯器會自動合成一個,但是編譯器合成的複製控制函數屬於位複製(淺度複製),如果類包含指針類型,就需要定義自己的複製控制函數,此時應當實現值複製(深度複製)。

 

只有單個形參,而且該形參爲本類類型的引用(常用const修飾),這樣的構造函數稱爲複製構造函數。

1. 對象的定義形式

string null_book = "9-999-99999-9";    

// copy-initialization 調用接收C風格字符串的構造函數構造一個string對象

// 然後使用複製構造函數將null_book初始化爲臨時對象

 

string null_book2 = string() ;     

// copy-initialization 調用默認構造函數構造一個string對象

// 然後使用複製構造函數將 null_book2初始化爲該臨時對象

 

2.形參與返回值

當形參或返回值類型爲類類型時由複製構造函數進行復制。

 

3.初始化容器元素

vector<string> svec(5); 

編譯器首先使用默認構造函數,構造一個臨時string對象,然後使用複製構造函數將臨時值複製到svec每個元素。

 

4.構造函數與數組元素

如果沒有爲類類型提供數組初始化式,將使用默認構造函數初始化每個元素,如果使用花括號來提供顯示初始化式,使用複製構造函數複製到對應元素。

 

3.1 合成的複製構造函數

如果沒有定義複製構造函數,編譯器就會合成一個,與默認構造函數不同的是,即使提供了其他構造函數,只要沒有定義複製構造函數,都會合成一個。合成的複製構造函數其行爲是:逐個成員初始化。

 

3.2 定義自己的複製構造函數

當類中具有指針類型,應當自己定義複製構造函數,而且複製構造函數一般不指定爲explict,指定了explicit不能使用等號進行隱式調用複製構造函數。

 

3.3 禁止複製

爲了禁止複製,類必須顯式聲明覆制構造函數爲private,然而類的友元和成員仍可以進行復制;

如果想要連友元和成員也禁止,可以聲明一個private複製構造函數而不對其定義,此時用戶代碼中的複製嘗試將會標記爲編譯錯誤,友元和成員的複製嘗試將在鏈接期導致錯誤。

 

3.4 賦值操作符

賦值時,對於一般的都是直接使用合成賦值函數。但是對於成員變量爲指針、對象或複製時希望完成其他附加操作的均需自己來處理賦值行爲。

 

3.5 析構函數

析構函數與複製構造函數或賦值操作符重要區別,即使定義了自己的析構函數,合成的析構函數仍然運行,在自定義函數之後運行。

 

三法則:需要析構函數,就需要複製構造函數和賦值操作符,因爲需要析構函數的場景通常爲成員變量爲指針、對象或希望完成其他附加操作的處理行爲。

  • 常見三種複製是一起出現的,即需要自定義一種複製行爲時,往往另外兩種也需要自定義
  • 需要禁止複製時,必須顯式地聲明其複製構造函數爲 private(其友元和成員依然可以複製)
  • 如果要連友元和成員的複製也禁止,就可以聲明一個private的複製構造函數但不對它進行定義。(如果複製類對象會提示編譯錯誤,如果成員和友元嘗試複製就會導致鏈接錯誤)
  • 在實現時其實賦值重載函數是包含複製構造函數和析構函數功能的,此時可以將複製和析構功能單獨做到兩個私有函數中,如演示代碼中的CopyData和DeleteData

定義了複製控制行爲的栗子:

class CCopyControl

{

public:

CCopyControl(int nData=0);

CCopyControl(const CCopyControl& c); //複製構造函數

CCopyControl& operator=(const CCopyControl& c); //賦值重載函數

~CCopyControl(); //析構函數

 

private:

int m_nData;

int *m_pData;

void CopyData(const CCopyControl& c);

void DeleteData();

};

 

//默認構造函數

CCopyControl::CCopyControl( int nData/*=0*/ )

{

m_nData = nData;

m_pData = new int(nData);

}

 

//複製構造函數

CCopyControl::CCopyControl( const CCopyControl& c )

{

CopyData(c);

}

 

//賦值重載函數

CCopyControl& CCopyControl::operator=( const CCopyControl& c )

{

if (this != &c)

{

DeleteData();

CopyData(c);

}

return *this;

}

 

//析構函數

CCopyControl::~CCopyControl()

{

DeleteData();

}

 

//複製數據

void CCopyControl::CopyData( const CCopyControl& c )

{

this->m_nData = c.m_nData;

this->m_pData = new int(c.m_nData); //深拷貝

}

 

//刪除數據

void CCopyControl::DeleteData()

{

if (!m_pData)

{

delete m_pData;

m_pData = NULL;

}

}

 

 

4 、智能指針

智能指針的使用栗子(如下定義中,也可以使指針計數維護類增加封裝類爲友元):

// CMySmartPtr指針封裝類,將普通指針封裝爲智能指針

template <class T>

class CMySmartPtr 

{

public:

/*構造函數*/

CMySmartPtr(T* pT)

{

pCountT = new CCountT(pT);

}

 

CMySmartPtr()

{

pCountT = NULL;//默認指針爲空

}

 

/*複製控制*/

~CMySmartPtr()

{

DeleteData();

}

 

CMySmartPtr(const CMySmartPtr& p)

{

CopyData(p);

}

 

CMySmartPtr& operator=(const CMySmartPtr& p)

{

if (this != &p) //注意自身對自身賦值的情況

{

DeleteData();

CopyData(p);

}

 

return *this;

}

 

/*指針*和->解引用*/

T& operator*()

{

return *(this->pCountT->pT);

}

 

T* operator->()

{

return this->pCountT->pT;

}

 

private:

//裝飾類,爲待管理的指針維護引用計數

class CCountT

{

public:

CCountT(T* pT)

{

this->pT = pT;

this->nCount = 1;

}

~CCountT()

{

delete pT;

}

T* pT;

int nCount;

};

 

//統一共享的指針,依靠引用計數來釋放

private:

CCountT *pCountT;

 

void CopyData(const CMySmartPtr& p)

{

p.pCountT->nCount++;

this->pCountT = p.pCountT;

}

 

void DeleteData()

{

if (pCountT && --pCountT->nCount==0)

{

delete pCountT;

pCountT = NULL;

}

}

};

 

 

來自 <http://www.360doc.com/showweb/0/0/548325597.aspx>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章