C++11——委託構造和繼承構造函數

1. 委託構造函數

委託構造函數允許使用同一個類中的一個構造函數調用其它的構造函數,從而簡化相關變量的初始化。下面舉例說明

#include <iostream>
using namespace std;

class Test
{
public:
    Test() {};
    Test(int max)
    {
        this->m_max = max > 0 ? max : 100;
    }

    Test(int max, int min)
    {
        this->m_max = max > 0 ? max : 100; // 冗餘代碼
        this->m_min = min > 0 && min < max ? min : 1;
    }

    Test(int max, int min, int mid)
    {
        this->m_max = max > 0 ? max : 100; // 冗餘代碼
        this->m_min = min > 0 && min < max ? min : 1; // 冗餘代碼
        this->m_middle = mid < max&& mid > min ? mid : 50;
    }

    int m_min;
    int m_max;
    int m_middle;
};

int main()
{
    Test t(90, 30, 60);
    cout << "min: " << t.m_min << ", middle: "
        << t.m_middle << ", max: " << t.m_max << endl;
    return 0;
}

在上面的程序中有三個構造函數,但是這三個函數中都有重複的代碼,在 C++11 之前構造函數是不能調用構造函數的,加入了委託構造之後,我們就可以輕鬆地完成代碼的優化了:

#include <iostream>
using namespace std;

class Test
{
public:
    Test() {};
    Test(int max)
    {
        this->m_max = max > 0 ? max : 100;
    }

    Test(int max, int min) :Test(max)
    {
        this->m_min = min > 0 && min < max ? min : 1;
    }

    Test(int max, int min, int mid) :Test(max, min)
    {
        this->m_middle = mid < max&& mid > min ? mid : 50;
    }

    int m_min;
    int m_max;
    int m_middle;
};

int main()
{
    Test t(90, 30, 60);
    cout << "min: " << t.m_min << ", middle: "
        << t.m_middle << ", max: " << t.m_max << endl;
    return 0;
}

在修改之後的代碼中可以看到,重複的代碼全部沒有了,並且在一個構造函數中調用了其他的構造函數用於相關數據的初始化,相當於是一個鏈式調用。在使用委託構造函數的時候還需要注意一些幾個問題:

這種鏈式的構造函數調用不能形成一個閉環(死循環),否則會在運行期拋異常。

如果要進行多層構造函數的鏈式調用,建議將構造函數的調用的寫在初始列表中而不是函數體內部,否則編譯器會提示形參的重複定義。

Test(int max)
{
    this->m_max = max > 0 ? max : 100;
}

Test(int max, int min)
{
    Test(max); // error, 此處編譯器會報錯, 提示形參max被重複定義
    this->m_min = min > 0 && min < max ? min : 1;
}

在初始化列表中調用了代理構造函數初始化某個類成員變量之後,就不能在初始化列表中再次初始化這個變量了。

// 錯誤, 使用了委託構造函數就不能再次m_max初始化了
Test(int max, int min) : Test(max), m_max(max)
{
    this->m_min = min > 0 && min < max ? min : 1;
}

2. 繼承構造函數
C++11 中提供的繼承構造函數可以讓派生類直接使用基類的構造函數,而無需自己再寫構造函數,尤其是在基類有很多構造函數的情況下,可以極大地簡化派生類構造函數的編寫。先來看沒有繼承構造函數之前的處理方式:

#include <iostream>
#include <string>
using namespace std;

class Base
{
public:
    Base(int i) :m_i(i) {}
    Base(int i, double j) :m_i(i), m_j(j) {}
    Base(int i, double j, string k) :m_i(i), m_j(j), m_k(k) {}

    int m_i;
    double m_j;
    string m_k;
};

class Child : public Base
{
public:
    Child(int i) :Base(i) {}
    Child(int i, double j) :Base(i, j) {}
    Child(int i, double j, string k) :Base(i, j, k) {}
};

int main()
{
    Child c(520, 13.14, "i love you");
    cout << "int: " << c.m_i << ", double: "
        << c.m_j << ", string: " << c.m_k << endl;
    return 0;
}

通過測試代碼可以看出,在子類中初始化從基類繼承的類成員,需要在子類中重新定義和基類一致的構造函數,這是非常繁瑣的,C++11 中通過添加繼承構造函數這個新特性完美的解決了這個問題,使得代碼更加精簡。

繼承構造函數的使用方法是這樣的:通過使用 using 類名::構造函數名(其實類名和構造函數名是一樣的)來聲明使用基類的構造函數,這樣子類中就可以不定義相同的構造函數了,直接使用基類的構造函數來構造派生類對象。

#include <iostream>
#include <string>
using namespace std;

class Base
{
public:
Base(int i) :m_i(i) {}
Base(int i, double j) :m_i(i), m_j(j) {}
Base(int i, double j, string k) :m_i(i), m_j(j), m_k(k) {}

int m_i;
double m_j;
string m_k;
};

class Child : public Base
{
public:
using Base::Base;
};

int main()
{
Child c1(520, 13.14);
cout << "int: " << c1.m_i << ", double: " << c1.m_j << endl;
Child c2(520, 13.14, "i love you");
cout << "int: " << c2.m_i << ", double: "
<< c2.m_j << ", string: " << c2.m_k << endl;
return 0;
}

 

#include <iostream>
#include <string>
using namespace std;

class Base
{
public:
    Base(int i) :m_i(i) {}
    Base(int i, double j) :m_i(i), m_j(j) {}
    Base(int i, double j, string k) :m_i(i), m_j(j), m_k(k) {}

    int m_i;
    double m_j;
    string m_k;
};

class Child : public Base
{
public:
    using Base::Base;
};

int main()
{
    Child c1(520, 13.14);
    cout << "int: " << c1.m_i << ", double: " << c1.m_j << endl;
    Child c2(520, 13.14, "i love you");
    cout << "int: " << c2.m_i << ", double: "
        << c2.m_j << ", string: " << c2.m_k << endl;
    return 0;
}

 

在修改之後的子類中,沒有添加任何構造函數,而是添加了 using Base::Base; 這樣就可以在子類中直接繼承父類的所有的構造函數,通過他們去構造子類對象了。

外如果在子類中隱藏了父類中的同名函數,也可以通過 using 的方式在子類中使用基類中的這些父類函數:

#include <iostream>
#include <string>
using namespace std;

class Base
{
public:
    Base(int i) :m_i(i) {}
    Base(int i, double j) :m_i(i), m_j(j) {}
    Base(int i, double j, string k) :m_i(i), m_j(j), m_k(k) {}

    void func(int i)
    {
        cout << "base class: i = " << i << endl;
    }

    void func(int i, string str)
    {
        cout << "base class: i = " << i << ", str = " << str << endl;
    }

    int m_i;
    double m_j;
    string m_k;
};

class Child : public Base
{
public:
    using Base::Base; //使用基類的構造函數 
    using Base::func; //修改基類的 函數
    void func()
    {
        cout << "child class: i'am luffy!!!" << endl;
    }
};

int main()
{
    Child c(250);
    c.func();
    c.func(19);
    c.func(19, "luffy");
    return 0;
}

上述示例代碼輸出的結果爲:

child class: i'am luffy!!!
base class: i = 19
base class: i = 19, str = luffy

子類中的 func() 函數隱藏了基類中的兩個 func() 因此默認情況下通過子類對象只能調用無參的 func(),在上面的子類代碼中添加了 using Base::func; 之後,就可以通過子類對象直接調用父類中被隱藏的帶參 func() 函數了。

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