C++入門教程(六十二):派生類對象的生命週期

小古銀的官方網站(完整教程):http://www.xiaoguyin.com/
C++入門教程視頻:https://www.bilibili.com/video/av20868986/

目錄

派生類對象的生命週期

基礎示例 1

#include <iostream>

class baseclass
{
public:
    baseclass(void)
    {
        std::cout << "基類構造函數執行中" << std::endl;
    }
    ~baseclass(void)
    {
        std::cout << "基類析構函數執行中" << std::endl;
    }
};

class derivedclass : public baseclass
{
public:
    derivedclass(void)
    {
        std::cout << "派生類構造函數執行中" << std::endl;
    }
    ~derivedclass(void)
    {
        std::cout << "派生類析構函數執行中" << std::endl;
    }
};

int main(void)
{
    {
        derivedclass obj;
    }
    return 0;
}

輸出結果:

基類構造函數執行中
派生類構造函數執行中
派生類析構函數執行中
基類析構函數執行中

基礎講解 1

由輸出結果可以看出,當不顯式調用基類的構造函數時,對象創建的時候也會調用基類的無參構造函數。

而調用順序是:當對象創建的時候,先調用基類的構造函數,然後再調用派生類的構造函數;當對象銷燬的時候,先調用派生類的析構函數,接着調用基類的析構函數。這樣就可以保證派生類對象的數據不會錯亂。

基礎示例 2

#include <iostream>
#include <string>

class baseclass
{
public:
    baseclass(void)
    {
        std::cout << "基類第一個構造函數執行中" << std::endl;
    }
    baseclass(int)
    {
        std::cout << "基類第二個構造函數執行中" << std::endl;
    }
    ~baseclass(void)
    {
        std::cout << "基類析構函數執行中" << std::endl;
    }
};

class derivedclass : public baseclass
{
public:
    derivedclass(void)
    {
        std::cout << "派生類第一個構造函數執行中" << std::endl;
    }
    derivedclass(int)
    {
        std::cout << "派生類第二個構造函數執行中" << std::endl;
    }
    ~derivedclass(void)
    {
        std::cout << "派生類析構函數執行中" << std::endl;
    }
};

int main(void)
{
    {
        derivedclass obj;
    }
    std::cout << std::endl;

    {
        derivedclass obj(1);
    }
    std::cout << std::endl;
    return 0;
}

輸出結果:

基類第一個構造函數執行中
派生類第一個構造函數執行中
派生類析構函數執行中
基類析構函數執行中

基類第一個構造函數執行中
派生類第二個構造函數執行中
派生類析構函數執行中
基類析構函數執行中

基礎講解 2

爲了區分基類和派生類的不同的構造函數,所以使用了參數重載了構造函數。因此,構造函數的參數可以忽略。

從輸出結果可以知道,只要沒有寫明調用基類的哪個構造函數,都會調用基類的無參構造函數。如果上面代碼刪除無參構造函數,即刪除基類第一個構造函數,那麼編譯將會錯誤。因爲編譯器要爲兩個派生類的構造函數添加調用基類無參構造函數的代碼,卻找不到基類的無參構造函數,不能創建和初始化基類的成員,所以報錯。

而調用順序也是:當對象創建的時候,先調用基類的構造函數,然後再調用派生類的構造函數;當對象銷燬的時候,先調用派生類的析構函數,接着調用基類的析構函數。這樣就可以保證派生類對象的數據不會錯亂。

基礎示例 3

#include <iostream>
#include <string>

class baseclass
{
public:
    baseclass(void)
    {
        std::cout << "基類第一個構造函數執行中" << std::endl;
    }
    baseclass(int)
    {
        std::cout << "基類第二個構造函數執行中" << std::endl;
    }
    ~baseclass(void)
    {
        std::cout << "基類析構函數執行中" << std::endl;
    }
};

class derivedclass : public baseclass
{
public:
    derivedclass(void)
    {
        std::cout << "派生類第一個構造函數執行中" << std::endl;
    }
    derivedclass(int)
        : baseclass(1)
    {
        std::cout << "派生類第二個構造函數執行中" << std::endl;
    }
    ~derivedclass(void)
    {
        std::cout << "派生類析構函數執行中" << std::endl;
    }
};

int main(void)
{
    {
        derivedclass obj;
    }
    std::cout << std::endl;

    {
        derivedclass obj(1);
    }
    std::cout << std::endl;
    return 0;
}

輸出結果:

基類第一個構造函數執行中
派生類第一個構造函數執行中
派生類析構函數執行中
基類析構函數執行中

基類第二個構造函數執行中
派生類第二個構造函數執行中
派生類析構函數執行中
基類析構函數執行中

基礎講解 3

爲了區分基類和派生類的不同的構造函數,所以使用了參數重載了構造函數。因此,構造函數的參數可以忽略。

由輸出結果可以看出,只要手動添加了需要調用基類的哪個構造函數,那麼編譯器就不會爲它添加調用基類無參構造函數的代碼。

基礎示例 4

#include <iostream>
#include <string>

class testclass
{
public:
    testclass(void)
    {
        std::cout << "測試構造函數執行中" << std::endl;
    }
    ~testclass(void)
    {
        std::cout << "測試析構函數執行中" << std::endl;
    }
};

class baseclass
{
public:
    baseclass(void)
    {
        std::cout << "基類構造函數執行中" << std::endl;
    }
    ~baseclass(void)
    {
        std::cout << "基類析構函數執行中" << std::endl;
    }
};

class derivedclass : public baseclass
{
public:
    derivedclass(void)
    {
        std::cout << "派生類第一個構造函數執行中" << std::endl;
    }
    derivedclass(int)
        : baseclass()
    {
        std::cout << "派生類第二個構造函數執行中" << std::endl;
    }
    derivedclass(int, int)
        : test()
        , baseclass()
    {
        std::cout << "派生類第三個構造函數執行中" << std::endl;
    }
    ~derivedclass(void)
    {
        std::cout << "派生類析構函數執行中" << std::endl;
    }
private:
    testclass test;
};

int main(void)
{
    {
        derivedclass obj;
    }
    std::cout << std::endl;

    {
        derivedclass obj(1);
    }
    std::cout << std::endl;

    {
        derivedclass obj(1, 1);
    }
    std::cout << std::endl;
    return 0;
}

輸出結果:

基類構造函數執行中
測試構造函數執行中
派生類第一個構造函數執行中
派生類析構函數執行中
測試析構函數執行中
基類析構函數執行中

基類構造函數執行中
測試構造函數執行中
派生類第二個構造函數執行中
派生類析構函數執行中
測試析構函數執行中
基類析構函數執行中

基類構造函數執行中
測試構造函數執行中
派生類第三個構造函數執行中
派生類析構函數執行中
測試析構函數執行中
基類析構函數執行中

基礎講解 4

爲了區分基類和派生類的不同的構造函數,所以使用了參數重載了構造函數。因此,構造函數的參數可以忽略。

派生類第一個構造函數派生類第二個構造函數進行對比,可知無論是否寫出基類構造函數調用代碼,它的執行順序都是先執行完基類構造函數,再創建和初始化派生類的成員變量;釋放則是先釋放派生類的成員變量內存,再調用基類的析構函數。

派生類第二個構造函數派生類第三個構造函數進行對比,可知它的調用順序和初始化列表的順序無關。

派生類生命週期總結

如果派生類構造函數沒有顯式調用基類構造函數,那麼編譯器就會在初始化列表中添加調用基類無參構造函數的代碼;如果派生類構造函數顯式地調用了基類構造函數,那麼就會執行提供的基類構造函數。

派生類對象的創建順序:基類成員變量創建和初始化 → 基類構造函數執行 → 派生類成員變量創建和初始化 → 派生類構造函數執行。

派生類對象的釋放順序:派生類析構函數執行 → 派生類成員變量釋放 → 基類析構函數執行 → 基類成員變量釋放。

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