基類的成員函式可以被繼承,可以通過派生類的物件訪問,但這僅僅指的是普通的成員函式,類的建構函式不能被繼承。建構函式不能被繼承是有道理的,因為即使繼承了,它的名字和派生類的名字也不一樣,不能成為派生類的建構函式,當然更不能成為普通的成員函式。
在設計派生類時,對繼承過來的成員變數的初始化工作也要由派生類的建構函式完成,但是大部分基類都有 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()
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):
m_score
(score),
people
(name, age)
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)
當然這段**只是為了方便大家理解,實際上這樣寫是錯誤的,因為基類建構函式不會被繼承,不能當做普通的成員函式來呼叫。換句話說,只能將基類建構函式的呼叫放在函式頭部,不能放在函式體中。
另外,函式頭部是對基類建構函式的呼叫,而不是宣告,所以括號裡的引數是實參,它們不但可以是派生類構造函式引數列表中的引數,還可以是區域性變數、常量等,例如:
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
()int
main
()
#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《執行結果:
***的年齡是0,成績是0。
小明的年齡是16,成績是90.5。
建立物件 stu1 時,執行派生類的建構函式student::student()
,它並沒有指明要呼叫基類的哪乙個建構函式,從執行結果可以很明顯地看出來,系統預設呼叫了不帶引數的建構函式,也就是people::people()
。
建立物件 stu2 時,執行派生類的建構函式student::student(char *name, int age, float score)
,它指明了基類的建構函式。
在第 27 行**中,如果將people(name, age)
去掉,也會呼叫預設建構函式,第 37 行的輸出結果將變為:
***的年齡是0,成績是90.5。
如果將基類 people 中不帶引數的建構函式刪除,那麼會發生編譯錯誤,因為建立物件 stu1 時需要呼叫 people 類的預設建構函式, 而 people 類中已經顯式定義了建構函式,編譯器不會再生成預設的建構函式。
C 子類建構函式
建構函式是用來初始化類物件的。如果類中沒有顯式的宣告建構函式,那麼編譯器會自動建立乙個預設的建構函式,並且這個預設的建構函式僅僅在沒有顯式的宣告建構函式的情況下才會被建立。建構函式與父類的其它成員不同,它不能被子類繼承。因此,在建立子類物件時,為了初始化從父類中繼承來的成員變數,編譯器需要呼叫其父類...
父類建構函式 子類建構函式
1.子類可以通過super關鍵字來顯式地呼叫父類的建構函式。2.當父類沒有提供無引數的建構函式時,子類也不可以有無參建構函式,且子類的建構函式中必須顯式的呼叫父類的建構函式 3.如果父類提供了無引數的建構函式,此時子類的建構函式就可以不顯式的呼叫父類的建構函式,預設呼叫父類的無參建構函式。4.只要父...
父類建構函式 子類建構函式
1.子類可以通過super關鍵字來顯式地呼叫父類的建構函式。2.當父類沒有提供無引數的建構函式時,子類也不可以有無參建構函式,且子類的建構函式中必須顯式的呼叫父類的建構函式 3.如果父類提供了無引數的建構函式,此時子類的建構函式就可以不顯式的呼叫父類的建構函式,預設呼叫父類的無參建構函式。4.只要父...