C++中基類與派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)

技術(shù)交流QQ群:1027579432,歡迎你的加入!

1.Cpp中的基類與派生類的構(gòu)造函數(shù)

  • 基類的成員函數(shù)可以被繼承,可以通過派生類的對象訪問,但這僅僅指的是普通的成員函數(shù),類的構(gòu)造函數(shù)不能被繼承。構(gòu)造函數(shù)不能被繼承是有道理的,因為即使繼承了,它的名字和派生類的名字也不一樣,不能成為派生類的構(gòu)造函數(shù),當然更不能成為普通的成員函數(shù)。
  • 在設(shè)計派生類時,對繼承過來的成員變量的初始化工作也要由派生類的構(gòu)造函數(shù)完成,但是大部分基類都有private屬性的成員變量,它們在派生類中無法訪問,更不能使用派生類的構(gòu)造函數(shù)來初始化。這種矛盾在C++繼承中是普遍存在的,解決這個問題的思路是:在派生類的構(gòu)造函數(shù)中調(diào)用基類的構(gòu)造函數(shù)。
  • 下面的例子展示了如何在派生類的構(gòu)造函數(shù)中調(diào)用基類的構(gòu)造函數(shù):
        #include "iostream"
    
        using namespace std;
    
        // 基類people
        class People{
            protected:
                char *m_name;
                int m_age;
            public:
                People(char *name, int age);
        };
        People::People(char *name, int age):m_name(name),m_age(age){}
    
        // 派生類student·
        class Student:public People{
            private:
                float m_score;
            public: 
                Student(char *name, int age, float score);
                void show();
        };
        // People(name, age)就是調(diào)用基類的構(gòu)造函數(shù)
        Student::Student(char *name, int age, float score):People(name, age),m_score(score){}
        void Student::show(){
            cout << "m_name = " << m_name << ",m_age = " << m_age << ",m_score = " << m_score << endl;
        }
    
    
        int main(){
            Student stu("curry", 30, 91.4);
            stu.show();
            return 0;
        }
    
  • People(name, age)就是調(diào)用基類的構(gòu)造函數(shù),并將name和age作為實參傳遞給它,m_score(score)是派生類的參數(shù)初始化表,它們之間以逗號,隔開。
        Student::Student(char *name, int age, float score):People(name, age),m_score(score){}
    
  • 也可以將基類構(gòu)造函數(shù)的調(diào)用放在參數(shù)初始化表后面:
        Student::Student(char *name, int age, float score): m_score(score), People(name, age){}
    
  • 但是不管它們的順序如何,派生類構(gòu)造函數(shù)總是先調(diào)用基類構(gòu)造函數(shù)再執(zhí)行其他代碼(包括參數(shù)初始化表以及函數(shù)體中的代碼),總體上看和下面的形式類似, 為了方便理解,實際上下面的寫法是錯誤的,因為基類構(gòu)造函數(shù)不會被繼承,不能當做普通的成員函數(shù)來調(diào)用。換句話說,只能將基類構(gòu)造函數(shù)的調(diào)用放在函數(shù)頭部,不能放在函數(shù)體中。:
        Student::Student(char *name, int age float score){
            People(name, age);
            m_score = score;
        }
    
  • 另外,函數(shù)頭部是對基類構(gòu)造函數(shù)的調(diào)用,而不是聲明,所以括號里的參數(shù)是實參,它們不但可以是派生類構(gòu)造函數(shù)參數(shù)列表中的參數(shù),還可以是局部變量、常量等,例如:
        Student::Student(char *name, int age, float score): People("小明", 16), m_score(score){ }
    

2.構(gòu)造函數(shù)的調(diào)用順序

  • 從上面的分析中可以看出,基類構(gòu)造函數(shù)總是被優(yōu)先調(diào)用,這說明創(chuàng)建派生類對象時,會先調(diào)用基類構(gòu)造函數(shù),再調(diào)用派生類構(gòu)造函數(shù),如果繼承關(guān)系有好幾層的話,例如:
        A----->B----->C
    
  • 構(gòu)造函數(shù)的調(diào)用順序是按照繼承的層次自頂向下、從基類再到派生類的。那么創(chuàng)建C類對象時構(gòu)造函數(shù)的執(zhí)行順序為:
        A類構(gòu)造函數(shù) --> B類構(gòu)造函數(shù) --> C類構(gòu)造函數(shù)
    
  • 派生類構(gòu)造函數(shù)中只能調(diào)用直接基類的構(gòu)造函數(shù),不能調(diào)用間接基類的。以上面的A、B、C 類為例,C是最終的派生類,B就是C的直接基類,A就是C的間接基類。因為在C中調(diào)用了B的構(gòu)造函數(shù),B又調(diào)用了A的構(gòu)造函數(shù),相當于C間接地(或者說隱式地)調(diào)用了A的構(gòu)造函數(shù),如果再在C中顯式地調(diào)用A的構(gòu)造函數(shù),那么A的構(gòu)造函數(shù)就被調(diào)用了兩次,相應(yīng)地,初始化工作也做了兩次,這不僅是多余的,還會浪費CPU時間以及內(nèi)存,毫無益處,所以C++禁止在C中顯式地調(diào)用A的構(gòu)造函數(shù)。

3.基類構(gòu)造函數(shù)調(diào)用規(guī)則

  • 通過派生類創(chuàng)建對象時必須要調(diào)用基類的構(gòu)造函數(shù),這是語法規(guī)定。換句話說,定義派生類構(gòu)造函數(shù)時最好指明基類構(gòu)造函數(shù);如果不指明,就調(diào)用基類的默認構(gòu)造函數(shù)(不帶參數(shù)的構(gòu)造函數(shù));如果沒有默認構(gòu)造函數(shù),那么編譯失敗。請看下面的例子:
        // 基類A
        class A{
            public:
                A();  // 基類默認構(gòu)造函數(shù)
                A(char *name, int age);
            protected:
                char *m_name;
                int m_age;
        };
    
        A::A():m_name("xxx"), m_age(30){}
        A::A(char *name, int age):m_name(name), m_age(age){}
    
        // 派生類B
        class B: public A{
            public:
                B();
                B(char *name, int age, float score);
                void show();
            private:
                float m_score;
        };
    
        B::B():m_score(0.0){}  // 派生類默認構(gòu)造函數(shù)
        B::B(char *name, int age, float score):A(name, age), m_score(score){}
        void B::show(){
            cout << "m_name = " << m_name << ",m_age = " << m_age << ",m_score = " << m_score << endl;
        }
    
    
    
        int main(){
            B b1;
            b1.show();
            cout << "-------------------------------\n";
    
            B b2("Curry", 30, 91.3);
            b2.show();
    
            return 0;
        }
    
  • 創(chuàng)建對象b1時,執(zhí)行派生類的構(gòu)造函數(shù)B::B(),它并沒有指明要調(diào)用基類的哪一個構(gòu)造函數(shù),從運行結(jié)果可以很明顯地看出來,系統(tǒng)默認調(diào)用了不帶參數(shù)的構(gòu)造函數(shù),也就是A::A()。創(chuàng)建對象b2時,執(zhí)行派生類的構(gòu)造函數(shù)B::B(char *name, int age, float score),它指明了基類的構(gòu)造函數(shù)。如果將B::B(char *name, int age, float score):A(name, age), m_score(score){}中的A(name, age)去掉,也會調(diào)用默認構(gòu)造函數(shù),輸出結(jié)果將變?yōu)椋簃_name = xxx, m_age=0, m_score=91.3。如果將基類A中不帶參數(shù)的構(gòu)造函數(shù)刪除,那么會發(fā)生編譯錯誤,因為創(chuàng)建對象b1時需要調(diào)用A類的默認構(gòu)造函數(shù), 而A類中已經(jīng)顯式定義了構(gòu)造函數(shù),編譯器不會再生成默認的構(gòu)造函數(shù)。

4.基類與派生類的析構(gòu)函數(shù)

  • 和構(gòu)造函數(shù)類似,析構(gòu)函數(shù)也不能被繼承。與構(gòu)造函數(shù)不同的是,在派生類的析構(gòu)函數(shù)中不用顯式地調(diào)用基類的析構(gòu)函數(shù),因為每個類只有一個析構(gòu)函數(shù),編譯器知道如何選擇,無需程序員干涉。
  • 另外析構(gòu)函數(shù)的執(zhí)行順序和構(gòu)造函數(shù)的執(zhí)行順序也剛好相反:
    • 創(chuàng)建派生類對象時,構(gòu)造函數(shù)的執(zhí)行順序和繼承順序相同,即先執(zhí)行基類構(gòu)造函數(shù),再執(zhí)行派生類構(gòu)造函數(shù)。
    • 而銷毀派生類對象時,析構(gòu)函數(shù)的執(zhí)行順序和繼承順序相反,即先執(zhí)行派生類析構(gòu)函數(shù),再執(zhí)行基類析構(gòu)函數(shù)。
  • 實例如下:
        // 基類和派生類的析構(gòu)函數(shù)
        class C{
            public:
                C(){
                    cout << "C constructor\n";
                }
                ~C(){
                    cout << "C deconstructor\n";
                }
        };
    
        class D: public C{
            public:
                D(){
                    cout << "D constructor\n";
                }
                ~D(){
                    cout << "D deconstructor\n";
                }
        };
    
        class E:public D{
            public:
                E(){
                    cout << "E constructor\n";
                }
                ~E(){
                    cout << "E deconstructor\n";
                }
        };
    
        int main(){
            E test;
            return 0;
        }
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容