前面我們說基類的成員函式可以被繼承,可以通過派生類的物件訪問,但這僅僅指的是普通的成員函式,類的建構函式不能被繼承。建構函式不能被繼承是有道理的,因為即使繼承了,它的名字和派生類的名字也不一樣,不能成為派生類的建構函式,當然更不能成為普通的成員函式。
在設計派生類時,對繼承過來的成員變數的初始化工作也要由派生類的建構函式完成,但是大部分基類都有 private 屬性的成員變數,它們在派生類中無法訪問,更不能使用派生類的建構函式來初始化。
這種矛盾在c++繼承中是普遍存在的,解決這個問題的思路是:在派生類的建構函式中呼叫基類的建構函式。
下面的例子展示了如何在派生類的建構函式中呼叫基類的建構函式:
#include
using namespace std;
//基類people
class people;
people::people(char *name, int age): m_name(name), m_age(age){}
//派生類student
class student: public people;
//people(name, age)就是呼叫基類的建構函式
student::student(char *name, int age, float score): people(name, age), m_score(score)
void student::display()
people(name, age)就是呼叫基類的建構函式,並將 name 和 age 作為實參傳遞給它,m_score(score)是派生類的引數初始化表,它們之間以逗號,隔開。
也可以將基類建構函式的呼叫放在引數初始化表後面:
student::student(char *name, int age, float score): m_score(score), people(name, age)
但是不管它們的順序如何,派生類建構函式總是先呼叫基類建構函式再執行其他**(包括引數初始化表以及函式體中的**),總體上看和下面的形式類似:
student::student(char *name, int age, float score)
當然這段**只是為了方便大家理解,實際上這樣寫是錯誤的,因為基類建構函式不會被繼承,不能當做普通的成員函式來呼叫。換句話說,只能將基類建構函式的呼叫放在函式頭部,不能放在函式體中。
另外,函式頭部是對基類建構函式的呼叫,而不是宣告,所以括號裡的引數是實參,它們不但可以是派生類構造函式引數列表中的引數,還可以是區域性變數、常量等,例如:
student::student(char *name, int age, float score): people(「小明」, 16), m_score(score)
建構函式的呼叫順序
從上面的分析中可以看出,基類建構函式總是被優先呼叫,這說明建立派生類物件時,會先呼叫基類建構函式,再呼叫派生類建構函式,如果繼承關係有好幾層的話,例如:
a --> b --> c
那麼建立 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 的建構函式。
基類構造函式呼叫規則
事實上,通過派生類建立物件時必須要呼叫基類的建構函式,這是語法規定。換句話說,定義派生類建構函式時最好指明基類建構函式;如果不指明,就呼叫基類的預設建構函式(不帶引數的建構函式);如果沒有預設建構函式,那麼編譯失敗。請看下面的例子:
純文字複製
#include
using namespace std;
//基類people
class people;
people::people(): m_name(「***」), m_age(0)
people::people(char name, int age): m_name(name), m_age(age){}
//派生類student
class student: public people;
student::student(): m_score(0.0) //派生類預設建構函式
student::student(char *name, int age, float score): people(name, age), m_score(score)
void student::display(){
cout《建立物件 stu1 時,執行派生類的建構函式student::student(),它並沒有指明要呼叫基類的哪乙個建構函式,從執行結果可以很明顯地看出來,系統預設呼叫了不帶引數的建構函式,也就是people::people()。
建立物件 stu2 時,執行派生類的建構函式student::student(char *name, int age, float score),它指明了基類的建構函式。
在第 27 行**中,如果將people(name, age)去掉,也會呼叫預設建構函式,第 37 行的輸出結果將變為:
***的年齡是0,成績是90.5。
C 基類和派生類的建構函式(詳解)
基類的成員函式可以被繼承,可以通過派生類的物件訪問,但這僅僅指的是普通的成員函式,類的建構函式不能被繼承。建構函式不能被繼承是有道理的,因為即使繼承了,它的名字和派生類的名字也不一樣,不能成為派生類的建構函式,當然更不能成為普通的成員函式。在設計派生類時,對繼承過來的成員變數的初始化工作也要由派生類...
基類與派生類的建構函式
一 預設建構函式的呼叫關係 通過下面的例子,我們來看一下基類與派生的建構函式的呼叫順序。建立時先基類後派生類。銷毀時先派生類後基類。include include using namespace std class cbase cbase class cderive public cbase cde...
c ,派生類無法呼叫基類建構函式
include include using namespace std class undergraduate void showinfo private char id 10 char name 10 char major 10 class graduate public undergraduat...