C++基类和派生类的构造函数(一)
前面我們說基類的成員函數可以被繼承,可以通過派生類的對象訪問,但這僅僅指的是普通的成員函數,類的構造函數不能被繼承。構造函數不能被繼承是有道理的,因為即使繼承了,它的名字和派生類的名字也不一樣,不能成為派生類的構造函數,當然更不能成為普通的成員函數。
在設計派生類時,對繼承過來的成員變量的初始化工作也要由派生類的構造函數完成,但是大部分基類都有 private 屬性的成員變量,它們在派生類中無法訪問,更不能使用派生類的構造函數來初始化。
這種矛盾在C++繼承中是普遍存在的,解決這個問題的思路是:在派生類的構造函數中調用基類的構造函數。
#include<iostream> using namespace std; //基類People class People{ protected:char *m_name;int m_age; public:People(char*, int); }; 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 display(); }; //People(name, age)就是調用基類的構造函數 Student::Student(char *name, int age, float score): People(name, age), m_score(score){ } void Student::display(){cout<<m_name<<"的年齡是"<<m_age<<",成績是"<<m_score<<"。"<<endl; } int main(){Student stu("小明", 16, 90.5);stu.display();return 0; }運行結果為:
小明的年齡是16,成績是90.5。
請注意第 23 行代碼:
Student::Student(char *name, int age, float score): People(name, age), m_score(score){ }People(name, age)就是調用基類的構造函數,并將 name 和 age 作為實參傳遞給它,m_score(score)是派生類的參數初始化表,它們之間以逗號,隔開。
也可以將基類構造函數的調用放在參數初始化表后面:
但是不管它們的順序如何,派生類構造函數總是先調用基類構造函數再執行其他代碼(包括參數初始化表以及函數體中的代碼),總體上看和下面的形式類似:
Student::Student(char *name, int age, float score){People(name, age);m_score = score; }當然這段代碼只是為了方便大家理解,實際上這樣寫是錯誤的,因為基類構造函數不會被繼承,不能當做普通的成員函數來調用。換句話說,只能將基類構造函數的調用放在函數頭部,不能放在函數體中。
另外,函數頭部是對基類構造函數的調用,而不是聲明,所以括號里的參數是實參,它們不但可以是派生類構造函數參數列表中的參數,還可以是局部變量、常量等,例如:
構造函數的調用順序
從上面的分析中可以看出,基類構造函數總是被優先調用,這說明創建派生類對象時,會先調用基類構造函數,再調用派生類構造函數,如果繼承關系有好幾層的話,例如:
那么創建 C 類對象時構造函數的執行順序為:
A類構造函數 --> B類構造函數 --> C類構造函數構造函數的調用順序是按照繼承的層次自頂向下、從基類再到派生類的。
還有一點要注意,派生類構造函數中只能調用直接基類的構造函數,不能調用間接基類的。以上面的 A、B、C 類為例,C 是最終的派生類,B 就是 C 的直接基類,A 就是 C 的間接基類。
C++ 這樣規定是有道理的,因為我們在 C 中調用了 B 的構造函數,B 又調用了 A 的構造函數,相當于 C 間接地(或者說隱式地)調用了 A 的構造函數,如果再在 C 中顯式地調用 A 的構造函數,那么 A 的構造函數就被調用了兩次,相應地,初始化工作也做了兩次,這不僅是多余的,還會浪費CPU時間以及內存,毫無益處,所以 C++ 禁止在 C 中顯式地調用 A 的構造函數。
總結
以上是生活随笔為你收集整理的C++基类和派生类的构造函数(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++继承时的对象内存位置(一)有成员变
- 下一篇: C++基类和派生类的构造函数(二)