1. 程式人生 > >C++各種建構函式的寫法【轉】

C++各種建構函式的寫法【轉】

 

(轉自:https://blog.csdn.net/baiyq369/article/details/54926983

建構函式 ,是一種特殊的方法 。主要用來在建立物件時初始化物件, 即為物件成員變數賦初始值,總與new運算子一起使用在建立物件的語句中 。特別的一個類可以有多個建構函式 ,可根據其引數個數的不同或引數型別的不同來區分它們 即建構函式的過載。(摘自百度百科 建構函式 )。

一、最基本的建構函式

class Base
{
public:
    Base(int var) : m_Var(var)
    {
    }
private:
    int m_Var;
};

以上建構函式的執行過程:

1)傳參   2)給類資料成員開闢空間     3)執行冒號語法給資料成員初始化    4)執行建構函式括號裡面的內容

這裡需要說明的是:冒號語法後面的內容相當於int a = 10;(初始化),而建構函式括號裡面則是相當於是int a; a = 10;(賦初值)

二、拷貝建構函式

class Base
{
public:
    Base(int var) : m_Var(var)
    {
    }
    //拷貝建構函式
    Base(Base &ref) : m_Var(ref.m_Var)
    {
    }
private:
    int m_Var;
};

為什麼拷貝建構函式的引數只能用引用呢?

這就要從拷貝建構函式式數碼時候觸發開始說起了,以下幾種情況都會自動呼叫拷貝建構函式:

1)用一個已有的物件初始化一個新物件的時候

2)將一個物件以值傳遞的方式傳給形參的時候

3)函式返回一個物件的時候

所以當一個物件以傳遞值的方式傳一個函式的時候,拷貝建構函式自動的被呼叫來生成函式中的物件。如果一個物件是被傳入自己的拷貝建構函式,它的拷貝建構函式將會被呼叫來拷貝這個物件,這樣複製才可以傳入它自己的拷貝建構函式,這會導致無限迴圈直至棧溢位。除了當物件傳入函式的時候被隱式呼叫以外,拷貝建構函式在物件被函式返回的時候也同樣的被呼叫。(摘自百度百科 

拷貝建構函式 )。

拷貝建構函式,一般不需要自己編寫,系統預設的拷貝建構函式就能抗住了,但是有些情況需要在構造的時候開闢空間,這時候就需要拷貝構造函數了,如下程式碼是摘自 林銳 博士的 高質量C++程式設計指南 一文。

class String
{
public:
    String(const char *str = NULL); // 普通建構函式
    String(const String &other);    // 拷貝建構函式
    ~ String(void);                 // 解構函式
private:
    char *m_data; // 用於儲存字串
};
// String 的解構函式
String::~String(void) 
{
    delete [] m_data;
    // 由於m_data 是內部資料型別,也可以寫成 delete m_data;
}

// String 的普通建構函式
String::String(const char *str) 
{
    if(str==NULL)
    {
        m_data = new char[1]; // 若能加 NULL 判斷則更好
        *m_data = '\0';
    }
    else
    {
        int length = strlen(str);
        m_data = new char[length+1]; // 若能加 NULL 判斷則更好
        strcpy(m_data, str);
    }
}
// 拷貝建構函式
String::String(const String &other) 
{
    int length = strlen(other.m_data);
    m_data = new char[length+1]; // 若能加 NULL 判斷則更好
    strcpy(m_data, other.m_data);
}

三、普通派生類建構函式的寫法

定義派生類物件的時候,會按如下步驟執行構造操作:

1)傳參     2)根據繼承時的宣告順序構造基類    3)給類資料成員開闢空間    4)執行冒號語法後面的語句    5)執行建構函式函式體語句

class Base
{
public:
   Base(int b) : m_b(b)
    {
    }
private:
    int m_b;
};

class Derived : public Base
{
public:
    //普通派生類建構函式的寫法
    Derived(int b, int d) : Base(b), m_d(d)
    {

    }
private:
    int m_d;
};

再寫一個多繼承的示例:

class Base1
{
public:
    Base1(int b1) : m_b1(b1)
    {
    }
private:
    int m_b1;
};

class Base2
{
public:
    Base2(int b2) : m_b2(b2)
    {
    }
private:
    int m_b2;
};

class Derived : public Base1, public Base2
{
public:
    Derived(int b1, int b2, int d) : Base1(b1), Base2(b2), m_d(d)
    { //注意冒號語法後面的順序無所謂,創造基類是按照上面的繼承宣告順序來進行的...
    }
private:
    int m_d;
};

四、含有虛繼承的派生類建構函式的寫法

為何要用到虛繼承?

虛繼承主要是針對多繼承時,出現二義性問題而提出的。比如,如下程式碼就需要用到虛繼承,否則的話Derived類繼承時,Base類就會不明確。

虛繼承建構函式的執行按照如下步驟:

1)傳參 2)建立基類,注意這時候需要顯示建立所有“有參建構函式”的基類,包括直接基類,間接基類。 3)給類資料成員開闢空間  4)執行冒號語法  5)執行建構函式函式體

注:你可能會疑惑,如下程式碼不是將Base間接基類建立了3次嗎?其實不是這樣的,編譯器是這樣處理的,當最遠的派生類Derived建立了基類Base之後,其直接基類建立Base類的語句將會被忽略掉。

class Base
{
public:
    Base(int b) : m_b(b)
    {
    }
private:
    int m_b;
};

class Base1 : virtual public Base
{
public:
    Base1(int b, int b1) : Base(b), m_b1(b1)
    {
    }
private:
    int m_b1;
};

class Base2 : virtual public Base
{
public:
    Base2(int b, int b2) : Base(b), m_b2(b2)
    {
    }
private:
    int m_b2;
};
//虛繼承,避免二義性
class Derived : public Base1, public Base2
{
public:
    Derived(int b, int b1, int b2, int d) : Base(b), Base1(b, b1), Base2(b, b2), m_d(d)
    { //注意冒號語法後面的順序無所謂,創造基類是按照上面的繼承宣告順序來進行的...
    }
private:
    int m_d;
};

五、關於虛析構

虛析構一般伴隨著多型而產生,多型主要方式就是用基類的指標或引用指向或引用派生類,而形成多型。

但是這樣就會存在一個問題,當我們析構的時候,由於是基類的指標,就會呼叫的是基類的解構函式,從而造成派生記憶體溢位。為了解決這個問題,引入了虛析構的概念。將基類的建構函式宣告為虛,從而使其在呼叫解構函式的時候能夠準確的呼叫派生類的解構函式。

如下程式碼必須用到虛析構才能準確的析構派生類,並釋放其佔有記憶體。

(直接的講,C++中基類採用virtual虛解構函式是為了防止記憶體洩漏。具體地說,如果派生類中申請了記憶體空間,並在其解構函式中對這些記憶體空間進行釋放。假設基類中採用的是非虛解構函式,當刪除基類指標指向的派生類物件時就不會觸發動態繫結,因而只會呼叫基類的解構函式,而不會呼叫派生類的解構函式。那麼在這種情況下,派生類中申請的空間就得不到釋放從而產生記憶體洩漏。所以,為了防止這種情況的發生,C++中基類的解構函式應採用virtual虛解構函式。)

class Base
{
public:
    Base(int b) : m_b(b)
    {
    }
    //虛析構,使基類指標能準確的釋放所指向的派生類裡面的內容
    virtual ~Base()
    {
    }
private:
    int m_b;
};

class Derived : public Base
{
public:
    Derived(int b, char *pStr) : Base(b)
    { 
        m_pStr = new char[strlen(pStr)+1];
        strcpy(m_pStr,pStr);
    }
    ~Derived()
    {
        delete m_pStr;
        m_pStr = NULL;
    }
private:
    char *m_pStr;
};

int main(void)
{
    char *pStr = "abcdefg";
    Base *b = new Derived(1,pStr);
    delete b;

    return 0;
}