轉(zhuǎn)自大神博客凡程子
一施逾、基本概念
- 類的繼承,是新的類從已有類那里得到已有的特性呈野〉桶或從已有類產(chǎn)生新類的過程就是類的派生,原有的類稱為基類或父類被冒,產(chǎn)生的新類稱為派生類或子類军掂。
- 派生類的聲明:
class 派生類名 : 繼承方式 基類名1,繼承方式 基類名2昨悼,...蝗锥,繼承方式 基類名n
{
派生類成員聲明;
} - 一個(gè)派生類可以同時(shí)有多個(gè)基類率触,這種情況稱為多重繼承终议,派生類只有一個(gè)基類,稱為單繼承葱蝗,直接派生穴张,間接派生。
- 繼承方式規(guī)定了如何訪問基類繼承的成員两曼。繼承方式有public皂甘,private,protected悼凑。如果不顯示給出繼承方式偿枕,默認(rèn)為private繼承。繼承方式指定了派生類成員以及類外對(duì)象對(duì)于從基類繼承來的成員的訪問權(quán)限户辫。
- 派生類繼承基類中除構(gòu)造和析構(gòu)函數(shù)以外的所有成員益老。
- 派生類生成:
- 吸收基類成員(除構(gòu)造析構(gòu)函數(shù)以外的所有成員)
- 改造基類成員(根據(jù)繼承方式調(diào)整基類成員的訪問,函數(shù)在子類中的覆蓋寸莫,以及虛函數(shù)在子類中的覆蓋)
- 添加新的成員
- public繼承(基類的private不是我的private)
當(dāng)類的繼承方式為公有繼承時(shí)捺萌,基類的公有和保護(hù)成員的訪問屬性在派生類中不變,而基類的私有成員不可訪問膘茎。即基類的公有成員和保護(hù)成員被繼承到派生類中仍作為派生類的公有成員和保護(hù)成員桃纯。派生類的其他成員可以直接訪問它們。無論派生類的成員還是派生類的對(duì)象都無法訪問基類的私有成員披坏。 - private繼承
當(dāng)類的繼承方式為私有繼承時(shí)态坦,基類中的公有成員和保護(hù)成員都以私有成員身份出現(xiàn)在派生類中,而基類的私有成員在派生類中不可訪問棒拂∩√荩基類的公有成員和保護(hù)成員被繼承后作為派生類的私有成員玫氢,派生類的其他成員可以直接訪問它們,但是在類外部通過派生類的對(duì)象無法訪問谜诫。無論是派生類的成員還是通過派生類的對(duì)象漾峡,都無法訪問從基類繼承的私有成員。通過多次私有繼承后喻旷,對(duì)于基類的成員都會(huì)成為不可訪問生逸。因此私有繼承比較少用。 - 保護(hù)繼承
保護(hù)繼承中且预,基類的公有成員和私有成員都以保護(hù)成員的身份出現(xiàn)在派生類中槽袄,而基類的私有成員不可訪問。派生類的其他成員可以直接訪問從基類繼承來的公有和保護(hù)成員锋谐,但是類外部通過派生類的對(duì)象無法訪問它們遍尺,無論派生類的成員還是派生類的對(duì)象,都無法訪問基類的私有成員涮拗。
二狮鸭、派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)
1.派生類中由基類繼承而來的成員的初始化工作還是由基類的構(gòu)造函數(shù)完成,然后派生類中新增的成員在派生類的構(gòu)造函數(shù)中初始化
- 派生類構(gòu)造函數(shù)的語法
派生類名::派生類名(參數(shù)總表):基類名1(參數(shù)表1)多搀,基類名(參數(shù)名2)....基類名n(參數(shù)名n)歧蕉,內(nèi)嵌子對(duì)象1(參數(shù)表1),內(nèi)嵌子對(duì)象2(參數(shù)表2)....內(nèi)嵌子對(duì)象n(參數(shù)表n)
{
派生類新增成員的初始化語句康铭;
}
注意:構(gòu)造函數(shù)的初始化順序并不以上面的順序進(jìn)行惯退,而是根據(jù)聲明的順序初始化 - 如果基類中沒有不帶參數(shù)的構(gòu)造函數(shù)(且參數(shù)沒有默認(rèn)初值),也可以說从藤,只有帶參數(shù)的構(gòu)造函數(shù)催跪,那么在派生類的構(gòu)造函數(shù)初始化列表里中必須調(diào)用基類構(gòu)造函數(shù),以初始化基類成員
- 派生類構(gòu)造函數(shù)執(zhí)行的次序:
- 調(diào)用基類構(gòu)造函數(shù)夷野,調(diào)用順序按照它們被繼承時(shí)聲明的順序(從左到右)
- 調(diào)用內(nèi)嵌成員對(duì)象的構(gòu)造函數(shù)懊蒸,調(diào)用順序按照它們?cè)陬愔新暶鞯捻樞?/li>
- 派生類的構(gòu)造函數(shù)體中的內(nèi)容
例子:
#include <iostream>
#include <time.h>
using namespace std;
class B1
{
public:
B1(int i)
{
cout<<"constructing B1 "<<i<<endl;
}
};
class B2
{
public:
B2(int j)
{
cout<<"constructing B2 "<<j<<endl;
}
};
class B3
{
public:
B3()
{
cout<<"constructing B3"<<endl;
}
};
class C: public B2, public B1, public B3
{
public:
C(int a, int b, int c, int d):B1(a), memberB2(d), memberB1(c),B2(b)
{
}
private:
B1 memberB1;
B2 memberB2;
B3 memberB3;
};
int main()
{
C obj(1,2,3,4);
return 0;
}
輸出結(jié)果為:
constructing B2 2
constructing B1 1
constructing B3
constructing B1 3
constructing B2 4
constructing B3
- 析構(gòu)函數(shù)
派生類的析構(gòu)函數(shù)的功能是在該對(duì)象消亡之前進(jìn)行一些必要的清理工作,析構(gòu)函數(shù)沒有類型悯搔,也沒有參數(shù)骑丸。析構(gòu)函數(shù)的執(zhí)行順序與構(gòu)造函數(shù)相反。
#include <iostream>
#include <time.h>
using namespace std;
class B1
{
public:
B1(int i)
{
cout<<"constructing B1 "<<i<<endl;
}
~B1()
{
cout<<"destructing B1"<<endl;
}
};
class B2
{
public:
B2(int j)
{
cout<<"constructing B2 "<<j<<endl;
}
~B2()
{
cout<<"destructing B2"<<endl;
}
};
class B3
{
public:
B3()
{
cout<<"constructing B3"<<endl;
}
~B3()
{
cout<<"destructing B3"<<endl;
}
};
class C: public B2, public B1, public B3
{
public:
C(int a, int b, int c, int d):B1(a), memberB2(d), memberB1(c),B2(b)
{
}
private:
B1 memberB1;
B2 memberB2;
B3 memberB3;
};
int main()
{
C obj(1,2,3,4);
return 0;
}
輸出結(jié)果為:
constructing B2 2
constructing B1 1
constructing B3
constructing B1 3
constructing B2 4
constructing B3
destructing B3
destructing B2
destructing B1
destructing B3
destructing B1
destructing B2
三妒貌、派生類成員的標(biāo)識(shí)和訪問
- 派生類成員屬性劃分為四種:不可訪問的成員通危,私有成員,保護(hù)成員灌曙,公有成員
- 作用域分辨
形式為:基類名::成員名菊碟;基類名::成員名(參數(shù)表)
如果某派生類的多個(gè)基類擁有同名的成員,同時(shí)派生類又新增這樣的同名成員在刺,在這種情況下逆害,派生類成員將隱藏所有基類的同名成員头镊,這就需要上面的調(diào)用方式才能調(diào)用基類的同名成員。
例子:多繼承同名
#include <iostream>
#include <time.h>
using namespace std;
class B1
{
public:
int nV;
void fun()
{
cout<<"member of B1 "<<nV<<endl;
}
};
class B2
{
public:
int nV;
void fun()
{
cout<<"member of B2 "<<nV<<endl;
}
};
class D1: public B1, public B2
{
public:
int nV;
void fun()
{
cout<<"member of D1 "<<nV<<endl;
}
};
int main()
{
D1 d1;
d1.nV = 1;
d1.fun();
d1.B1::nV = 2;
d1.B1::fun();
d1.B2::nV = 3;
d1.B2::fun();
return 0;
}
輸出結(jié)果為:
member of D1 1
member of B1 2
member of B2 3
以上通過作用域分辨符魄幕,解決了訪問基類中被屏蔽的同名成員相艇。
- 如果某個(gè)派生類的部分或全部直接基類是從另一個(gè)共同的基類派生而來,在這些直接基類中梅垄,從上一級(jí)基類繼承來的成員就擁有相同的名稱,因此派生類中也就會(huì)產(chǎn)生同名現(xiàn)象输玷,對(duì)這種類型的同名成員也要使用作用域分辨符來唯一標(biāo)識(shí)队丝,而且必須用直接基類進(jìn)行限定。
#include <iostream>
#include <time.h>
using namespace std;
class B0
{
public:
int nV;
void fun()
{
cout<<"member of B0 "<<nV<<endl;
}
};
class B1:public B0
{
public:
int nV1;
};
class B2:public B0
{
public:
int nV2;
};
class D1:public B1, public B2 //B1 B2是直接基類
{
public:
int nVd;
void fund()
{
cout<<"member of D1"<<endl;
}
};
int main()
{
D1 d1;
d1.B1::nV = 2;
d1.B1::fun();
d1.B2::nV = 3;
d1.B2::fun();
return 0;
}
輸出結(jié)果為:
member of B0 2
member of B0 3
在這種情況下欲鹏,派生類對(duì)象在內(nèi)存中就同時(shí)擁有成員nV及fun的兩份拷貝机久。但是很多情況下,我們只需要這樣一個(gè)這樣的數(shù)據(jù)拷貝赔嚎,同一成員的多份拷貝增加了內(nèi)存的開銷膘盖。可以通過虛函數(shù)來解決這個(gè)問題尤误。
- 虛基類(針對(duì)多重繼承)
為了解決前面提到的多重拷貝的問題侠畔,可以將共同基類設(shè)置為虛基類,這時(shí)從不同的路徑繼承過來的同名數(shù)據(jù)成員在內(nèi)存中就只有一個(gè)拷貝损晤,同一個(gè)函數(shù)也只有一個(gè)映射软棺。
虛基類的聲明是在派生類的聲明過程,其語法形式為:
class 派生類名::virtual 繼承方式 基類名尤勋;
例子:
#include <iostream>
#include <time.h>
using namespace std;
class B0
{
public:
int nV;
void fun()
{
cout<<"member of B0 "<<nV<<endl;
}
};
class B1:virtual public B0
{
public:
int nV1;
};
class B2:virtual public B0
{
public:
int nV2;
};
class D1:public B1, public B2
{
public:
int nVd;
void fund()
{
cout<<"member of D1"<<endl;
}
};
int main()
{
D1 d1;
d1.nV = 2;
d1.fun();
return 0;
}
輸出結(jié)果為:
member of B0 2
- 虛函數(shù)及其派生類的構(gòu)造函數(shù)
一般而言喘落,派生類只對(duì)其直接基類的構(gòu)造函數(shù)傳遞參數(shù),但是在虛基類中最冰,不管是直接或間接虛基類的所有派生類瘦棋,都必須在構(gòu)造函數(shù)的成員初始化列表中列出對(duì)虛基類的初始化。
#include <iostream>
#include <time.h>
using namespace std;
class B0
{
public:
B0(int n)
{
nV = n;
}
int nV;
void fun()
{
cout<<"member of B0 "<<nV<<endl;
}
};
class B1:virtual public B0
{
public:
B1(int a):B0(a)
{
}
int nV1;
};
class B2:virtual public B0
{
public:
B2(int a):B0(a)
{
}
int nV2;
};
class D1:public B1, public B2
{
public:
D1(int a):B0(a), B1(a), B2(a)
{
}
int nVd;
void fund()
{
cout<<"member of D1"<<endl;
}
};
int main()
{
D1 d1(1);
d1.nV = 2;
d1.fun();
return 0;
}
以上例子看上去B0的構(gòu)造函數(shù)好像被調(diào)用了三次暖哨,但是實(shí)際上只有D1類中的D1(int a):B0(a), B1(a), B2(a)
才是真正的調(diào)用了B0構(gòu)造函數(shù)赌朋。
四、賦值兼容規(guī)則
- 賦值兼容規(guī)則是指在需要基類對(duì)象的任何地方都可以使用公有派生類的對(duì)象來替代篇裁。
- 賦值兼容規(guī)則所指的替代包括:
- 派生類的對(duì)象可以賦值給基類對(duì)象
- 派生類的對(duì)象可以初始化基類的引用
- 派生類對(duì)象的地址可以賦給指向基類的指針
- 在替代之后箕慧,派生類對(duì)象就可以作為基類的對(duì)象使用,但只能使用從基類繼承的成員茴恰。
例子:
#include <iostream>
#include <time.h>
using namespace std;
class B0
{
public:
void display()
{
cout<<"B0::display()"<<endl;
}
};
class B1:public B0
{
public:
void display()
{
cout<<"B1::display()"<<endl;
}
};
class B2:public B0
{
public:
void display()
{
cout<<"B2::display()"<<endl;
}
};
void fun(B0 *ptr)
{
ptr->display();
}
int main()
{
B0 b0;
B1 b1;
B2 b2;
fun(&b0);
b0 = b1;
fun(&b0);
b0 = b2;
fun(&b0);
return 0;
}
輸出結(jié)果為:
B0::display()
B0::display()
B0::display()
通過這種賦值兼容后颠焦,每次調(diào)用的同名函數(shù)都是基類的同名函數(shù),如果想調(diào)用派生類的往枣,則需要使用虛函數(shù)伐庭。