一、封裝
把客觀事物封裝成類,而且可以把自己的數(shù)據(jù)和方法設(shè)置為只能讓可信的類或者對(duì)象操作,對(duì)不可信的信息進(jìn)行隱藏(利用public,private,protected,friend)實(shí)現(xiàn)
二、繼承
2.1類與類的關(guān)系
- has-a :描述一個(gè)類由多個(gè)部件類構(gòu)成,一個(gè)類的成員屬性是另一個(gè)已經(jīng)定義好的類。
- use-a:一個(gè)類使用另一個(gè)類,通過(guò)類之間的成員函數(shù)相互聯(lián)系,定義友元或者通過(guò)傳遞參數(shù)的方式來(lái)實(shí)現(xiàn)。
- is-a:繼承,關(guān)系具有傳遞性
2.2繼承
讓某個(gè)類型對(duì)象獲得另一個(gè)類型對(duì)象的方法,可以使用現(xiàn)有類的所有功能。有三種繼承方法:
2.2.1 實(shí)現(xiàn)繼承
使用基類的屬性,不需要額外功能
2.2.2 接口繼承
僅使用屬性和方法的名稱,子類提供實(shí)現(xiàn)的能力
2.2.3 可視繼承
子類使用基類外觀和實(shí)現(xiàn)代碼的能力(C++不常用)
2.3 繼承機(jī)制中的對(duì)象之間如何轉(zhuǎn)換?指針和引用之間如何轉(zhuǎn)換?
- 將派生類指針或引用轉(zhuǎn)換為基類的指針或者引用被稱為向上類型轉(zhuǎn)換,向上類型轉(zhuǎn)換會(huì)自動(dòng)進(jìn)行,而且是類型安全的。
- 將基類指針或者引用轉(zhuǎn)換為派生類指針或者引用被稱為向下類型轉(zhuǎn)換,向下類型轉(zhuǎn)換不會(huì)自動(dòng)進(jìn)行,因?yàn)橐粋€(gè)基類對(duì)應(yīng)幾個(gè)派生類,向下不知道會(huì)對(duì)應(yīng)哪個(gè)派生類,所以在向下類型轉(zhuǎn)換時(shí)必須加動(dòng)態(tài)類型識(shí)別技術(shù),RTTI技術(shù),用dynamic_cast向下類型轉(zhuǎn)換。
2.4 基類
如果想要將某個(gè)類用作基類,為什么這個(gè)類必須定義而非聲明?
派生類中會(huì)包含并且使用從基類繼承而來(lái)的成員,為了使用這些成員,派生類必須知道他們是什么。
什么是虛擬繼承?
代碼來(lái)源阿秀的學(xué)習(xí)筆記
#include <iostream>
using namespace std;
class A{}
class B : virtual public A{};
class C : virtual public A{};
class D : public B, public C{};
int main()
{
cout << "sizeof(A):" << sizeof A <<endl; // 1,空對(duì)象,只有一個(gè)占位
cout << "sizeof(B):" << sizeof B <<endl; // 4,一個(gè)bptr指針,省去占位,不需要對(duì)齊
cout << "sizeof(C):" << sizeof C <<endl; // 4,一個(gè)bptr指針,省去占位,不需要對(duì)齊
cout << "sizeof(D):" << sizeof D <<endl; // 8,兩個(gè)bptr,省去占位,不需要對(duì)齊
}
B和C虛擬繼承A,D公有繼承B和C,這種方式是一種菱形繼承或者鉆石繼承。虛擬繼承情況下,無(wú)論基類被繼承多少次,只會(huì)存在一個(gè)實(shí)體。所以D類只會(huì)包含一個(gè)A類對(duì)象,避免重復(fù)成員出現(xiàn),減少了內(nèi)存使用。 虛擬繼承基類的子類中,子類會(huì)增加某種形式的指針,或者指向虛基類子對(duì)象,或者指向一個(gè)相關(guān)的表格;表格中存放的不是虛基類子對(duì)象的地址,就是其偏移量,此類指針被稱為bptr。如果既存在vptr又存在bptr,那么編譯器會(huì)將其優(yōu)化,合并為一個(gè)指針。
當(dāng)一個(gè)類使用虛擬繼承,它的構(gòu)造函數(shù)需要顯示調(diào)用虛基類的構(gòu)造函數(shù),同時(shí)虛基類的構(gòu)造函數(shù)會(huì)在最底層的派生類構(gòu)造函數(shù)中進(jìn)行調(diào)用,而不是在中間類的構(gòu)造函數(shù)中調(diào)用。
2.5 抽象基類為什么不能創(chuàng)建對(duì)象?
因?yàn)槌橄箢惏藳](méi)有定義的純虛函數(shù),所以不能定義抽象類的對(duì)象。
2.6 多繼承
什么是多繼承
C++允許為一個(gè)派生類指定多個(gè)基類,這樣的繼承結(jié)構(gòu)被稱為多重繼承。
優(yōu)點(diǎn)
對(duì)象可以調(diào)用多個(gè)基類中的接口。
缺點(diǎn)
派生類所繼承的多個(gè)基類有相同的基類,派生類對(duì)象需要調(diào)用這個(gè)祖先類的接口方法,就會(huì)出現(xiàn)二義性。
解決方法
- 加上全局符確定調(diào)用哪一份拷貝。比如pa.Author::eat()調(diào)用屬于Author的拷貝
- 虛擬繼承
2.7 public,private,protected訪問(wèn)權(quán)限
圖片來(lái)源阿秀的學(xué)習(xí)筆記
2.8 組合與繼承的比較
繼承有以下缺點(diǎn):
- 父類內(nèi)部對(duì)子類不可見(jiàn)
- 子類從父類繼承的方法在編譯的時(shí)候就確定了,所以無(wú)法在運(yùn)行期間改變父類繼承的方法的行為
- 如果父類的方法做了修改(比如增加一個(gè)參數(shù)),則子類的方法必須做出相應(yīng)的修改。父類與子類是一種高耦合,違背了面向?qū)ο蟮乃枷搿?/li>
組合
組合就是設(shè)計(jì)類的時(shí)候把要組合的類的對(duì)象加入到這個(gè)類中作為自己的成員變量。
組合的優(yōu)點(diǎn)
- 當(dāng)前對(duì)象只能通過(guò)所包含的那個(gè)對(duì)象去調(diào)用其方法,所包含的對(duì)象的內(nèi)部細(xì)節(jié)對(duì)當(dāng)前對(duì)象是不可見(jiàn)的。
- 當(dāng)前對(duì)象與包含的對(duì)象是一個(gè)低耦合的關(guān)系
- 當(dāng)前對(duì)象可以在運(yùn)行的時(shí)候動(dòng)態(tài)綁定所包含的對(duì)象,通過(guò)set方法給所包含的對(duì)象賦值。
#include <iostream>
class Base {
public:
virtual void display() = 0;
};
class SubClassA : public Base {
public:
void display() override {
std::cout << "This is SubClassA" << std::endl;
}
};
class SubClassB : public Base {
public:
void display() override {
std::cout << "This is SubClassB" << std::endl;
}
};
class Container {
private:
Base* containedObject;
public:
void setContainedObject(Base* object) {
containedObject = object;
}
void displayContainedObject() {
containedObject->display();
}
};
int main() {
SubClassA objA;
SubClassB objB;
Container container;
// 綁定SubClassA對(duì)象到Container中
container.setContainedObject(&objA);
container.displayContainedObject(); // 輸出: This is SubClassA
// 綁定SubClassB對(duì)象到Container中
container.setContainedObject(&objB);
container.displayContainedObject(); // 輸出: This is SubClassB
return 0;
}
組合的缺點(diǎn)
- 容易產(chǎn)生過(guò)多對(duì)象
- 為了能組合多個(gè)對(duì)象,必須仔細(xì)對(duì)接口定義。
三、多態(tài)
同一事物表現(xiàn)不同事物的能力。允許將子類類型指針賦值給父類類型指針
多態(tài)有兩種-重載和虛函數(shù)。
3.1 實(shí)現(xiàn)多態(tài)的兩種方法
1、重載(編譯時(shí)多態(tài))
編譯時(shí)多態(tài),允許存在多個(gè)同名函數(shù),而這些函數(shù)的參數(shù)表不同,不能出現(xiàn)參數(shù)個(gè)數(shù)和類型均相同,僅僅依靠返回值不同來(lái)區(qū)分的函數(shù)(參數(shù)個(gè)數(shù)或者參數(shù)類型不同)
重載是在同一范圍定義中的同名成員函數(shù)才存在重載關(guān)系。重載和函數(shù)成員是否是虛函數(shù)沒(méi)有關(guān)系
程序中有函數(shù)重載的時(shí)候函數(shù)的匹配原則和順序是什么?
- 名字查找
- 確定候選函數(shù)
- 尋找最佳匹配
重載運(yùn)算符
只能重載已經(jīng)有的運(yùn)算符。有兩種重載方式:成員運(yùn)算符和非成員運(yùn)算符,成員運(yùn)算符比非成員運(yùn)算符少一個(gè)參數(shù),下標(biāo)運(yùn)算符(通常以所訪問(wèn)元素的引用作為返回值,最好定義下標(biāo)運(yùn)算符的常量版本和非常量版本)、箭頭運(yùn)算符(解引用通常也是類的成員,重載的箭頭運(yùn)算符必須返回類的指針)必須是成員運(yùn)算符。引入運(yùn)算符重載是為了實(shí)現(xiàn)類的多態(tài)性。
- 示例一
#include <iostream>
using namespace std;
class Complex {
public:
int real, imag;
Complex(int r = 0, int i =0) {real = r; imag = i;}
Complex operator + (Complex const &obj) {
Complex res;
res.real = real + obj.real;
res.imag = imag + obj.imag;
return res;
}
void print() { cout << real << " + i" << imag << endl; }
};
Complex operator - (Complex const &obj1, Complex const &obj2) {
Complex res;
res.real = obj1.real - obj2.real;
res.imag = obj1.imag - obj2.imag;
return res;
}
int main()
{
Complex c1(10, 5), c2(2, 4);
Complex c3 = c1 + c2;
c3.print();//12+i9
Complex c4 = c1 - c2;
c4.print();//8+i1
}
- 示例二
#include <iostream>
using namespace std;
class MyClass {
int arr[5];
public:
MyClass() {
for (int i = 0; i < 5; i++)
arr[i] = i;
}
int operator[](int i) {
return arr[i];
}
};
class PtrClass {
MyClass *ptr;
public:
PtrClass(MyClass *p = NULL) {
ptr = p;
}
MyClass* operator->() {
return ptr;
}
};
int main() {
MyClass obj;
PtrClass ptr(&obj);
cout << obj[2] << endl;//2
cout << ptr->operator[](2) << endl;//2
}
- 解引用
在 C++ 中,解引用運(yùn)算符 * 用于訪問(wèn)指針指向的變量的值。例如,如果您有一個(gè)指向整型變量的指針 p,則可以使用表達(dá)式 *p 來(lái)訪問(wèn)該變量的值。
#include <iostream>
using namespace std;
int main() {
int x = 5;
int *p = &x;
cout << *p << endl;//5
*p = 10;
cout << x << endl;//10
}
2、虛函數(shù)(覆蓋)(重寫(xiě))(運(yùn)行時(shí)多態(tài))
運(yùn)行時(shí)多態(tài)。子類重新定義父類的虛函數(shù)。在基類的函數(shù)前加上virtual關(guān)鍵字,在派生類中重寫(xiě)該函數(shù),運(yùn)行的時(shí)候根據(jù)所指對(duì)象是基類還是基類還是派生類來(lái)調(diào)用里面的函數(shù)。
注意
- 與基類的虛函數(shù)有相同的參數(shù)個(gè)數(shù)
- 與基類的虛函數(shù)有相同的參數(shù)類型
- 與基類的虛函數(shù)有相同的返回值類型
final和override關(guān)鍵字
- override
這個(gè)關(guān)鍵字的意思是,這個(gè)子類的這個(gè)函數(shù)是重寫(xiě)父類的,如果不小心打錯(cuò)了名字,編譯是不會(huì)通過(guò)的 - final
不希望某個(gè)類被繼承或者不希望某個(gè)虛函數(shù)被重寫(xiě),可以在類名和虛函數(shù)后面添加final關(guān)鍵字。添加final關(guān)鍵字之后如果被繼承或者重寫(xiě),編譯器會(huì)報(bào)錯(cuò)
代碼來(lái)源阿秀的學(xué)習(xí)筆記
class Base
{
virtual void foo();
};
class A : public Base
{
void foo() final; // foo 被override并且是最后一個(gè)override,在其子類中不可以重寫(xiě)
};
class B final : A // 指明B是不可以被繼承的
{
void foo() override; // Error: 在A中已經(jīng)被final了
};
class C : B // Error: B is final
{
};
虛函數(shù)底層實(shí)現(xiàn)原理
虛表:類中含有virtual關(guān)鍵詞時(shí),系統(tǒng)自動(dòng)生成虛表
虛表指針:含有虛函數(shù)的類實(shí)例化對(duì)象時(shí),對(duì)象地址前四個(gè)字節(jié)存儲(chǔ)指向虛表的指針
所以構(gòu)造函數(shù)不能定義為虛函數(shù),因?yàn)樘摵瘮?shù)對(duì)應(yīng)一個(gè)虛函數(shù)表,類中存儲(chǔ)一個(gè)虛表指針,而此時(shí)還沒(méi)有初始化,所以沒(méi)有虛表指針,無(wú)法找到虛表。
虛表的特征
- 全局共享,也就是全局只有一個(gè),在編譯的時(shí)候就構(gòu)造完成。
- 虛表類似于一個(gè)數(shù)組,類對(duì)象中存儲(chǔ)vptr指針,指向虛函數(shù)表。
- 虛表存儲(chǔ)的是虛函數(shù)的地址即虛函數(shù)表的元素是指向類成員函數(shù)的指針,而類中虛函數(shù)的個(gè)數(shù)在編譯的時(shí)候就可以確定,虛函數(shù)表的大小是在編譯的時(shí)候確定的,不用動(dòng)態(tài)分配內(nèi)存空間。
虛函數(shù)表存放位置
虛函數(shù)表類似于類中的靜態(tài)成員的變量,靜態(tài)成員變量也是全局共享。
測(cè)試結(jié)果顯示:虛函數(shù)表vtable在Linux中存放在可執(zhí)行文件的只讀數(shù)據(jù)段(也就是常量區(qū))中;微軟編譯器將虛函數(shù)表存放在常量段。虛函數(shù)位于代碼區(qū)
虛函數(shù)實(shí)現(xiàn)多態(tài)的過(guò)程
- 編譯器發(fā)現(xiàn)基類中有虛函數(shù),自動(dòng)生成一個(gè)虛函數(shù)表,這個(gè)是一個(gè)一維數(shù)組,虛表保存虛函數(shù)入口地址
- 編譯器會(huì)在每個(gè)對(duì)象前四個(gè)字節(jié)中保存一個(gè)虛表指針,構(gòu)造時(shí),根據(jù)對(duì)象類型初始化虛表指針。
- 派生類定義對(duì)象的時(shí)候,程序運(yùn)行會(huì)自動(dòng)調(diào)用構(gòu)造函數(shù),構(gòu)造子類對(duì)象的時(shí)候,先調(diào)用父類構(gòu)造函數(shù),然后才調(diào)用子類的構(gòu)造函數(shù),為子類對(duì)象初始化虛表指針,令他指向子類虛表
- 派生類對(duì)基類沒(méi)有重寫(xiě)的時(shí)候,派生類虛表指針指向基類的虛表,重寫(xiě)的時(shí)候,指向自身的虛表
構(gòu)造函數(shù)或者析構(gòu)函數(shù)能否調(diào)用虛函數(shù)?
派生類對(duì)象構(gòu)造期間進(jìn)入基類的構(gòu)造函數(shù)時(shí),對(duì)象類型變成了基類類型,而不是派生類型,同樣進(jìn)入基類析構(gòu)函數(shù)時(shí),對(duì)象也是基類類型。所以,虛函數(shù)始終僅僅調(diào)用基類的虛函數(shù),不能達(dá)到多態(tài)的效果。
虛表指針和this指針關(guān)系
this指針表示對(duì)象的地址起始內(nèi)存地址。即this指針的值,指向了對(duì)象起始內(nèi)存。this指針的值和類第一個(gè)成員變量的地址一樣。當(dāng)類有虛函數(shù)時(shí)候,類的第一個(gè)成員變量是一個(gè)虛函數(shù)指針(_vfpr),根據(jù)this指針的值和第一個(gè)成員變量地址相同(this指針指向第一個(gè)成員變量)。因此當(dāng)有虛函數(shù)時(shí),this指針的值等于虛函數(shù)指針的地址(this指針指向虛函數(shù)指針)。
虛函數(shù)調(diào)用過(guò)程
- 將this指針轉(zhuǎn)換為int*類型,并解引用得到虛表指針
- 將虛表指針加上offset得到虛函數(shù)指針
- 將虛函數(shù)指針轉(zhuǎn)換為對(duì)應(yīng)類型,并傳入this指針作為參數(shù),調(diào)用虛函數(shù)
3、重載和重寫(xiě)的區(qū)別
- 重寫(xiě)是父類和子類的垂直關(guān)系,重載是不同函數(shù)之間的水平關(guān)系。
- 重寫(xiě)要求參數(shù)列表相同,重載則要求參數(shù)列表不同,返回值不要求。
- 重寫(xiě)關(guān)系中,調(diào)用方法根據(jù)對(duì)象類型決定,重載根絕調(diào)用的時(shí)候?qū)崊⒈砼c形參表對(duì)應(yīng)關(guān)系來(lái)選擇函數(shù)體
4、隱藏
隱藏指的是子類隱藏父類的函數(shù)(還存在),具有以下特征
- 子類函數(shù)與父類名稱相同,但是參數(shù)不相同,父類函數(shù)被隱藏
- 子類函數(shù)與父類函數(shù)名稱相同,參數(shù)也相同,但是父類函數(shù)沒(méi)有virtual,父類函數(shù)被隱藏
- 兩個(gè)函數(shù)參數(shù)相同,但是基類函數(shù)不是虛函數(shù),和重寫(xiě)的區(qū)別在于基類函數(shù)是否是虛函數(shù)
- 兩個(gè)函數(shù)參數(shù)不同,無(wú)論基類函數(shù)是不是虛函數(shù),都會(huì)被隱藏。和重載的區(qū)別在于兩個(gè)函數(shù)不在同一個(gè)類中
舉例一
代碼來(lái)源C++中函數(shù)重載、隱藏、覆蓋和重寫(xiě)的區(qū)別
#include <iostream>
using namespace std;
void func(char* s){
cout<<"global function with name:"<<s<<endl;
}
class A{
void func(){
cout<<"member function of A"<<endl;
}
public:
void useFunc(){
//func("lvlv");//A::func()將外部函數(shù)func(char*)隱藏
func();
::func("lvlv");
}
virtual void print(){
cout<<"A's print"<<endl;
}
};
class B:public A{
public:
void useFunc(){ //隱藏A::vodi useFunc()
cout<<"B's useFunc"<<endl;
}
int useFunc(int i){ //隱藏A::vodi useFunc()
cout<<"In B's useFunc(),i="<<i<<endl;
return 0;
}
virtual int print(char* a){
cout<<"B's print:"<<a<<endl;
return 1;
}
//下面編譯不通過(guò),因?yàn)閷?duì)父類虛函數(shù)重寫(xiě)時(shí),需要函數(shù)返回值類型,函數(shù)名稱和參數(shù)類型全部相同才行
// virtual int print(){
// cout<<"B's print:"<<a<<endl;
// }
};
int main(){
A a;
a.useFunc();
B b;
b.useFunc();//A::useFunc()被B::useFunc()隱藏
b.A::useFunc();
b.useFunc(2);
//b.print();//編譯出錯(cuò),A::print()被B::print(char* a)隱藏
b.A::print();
b.print("jf");
}
程序運(yùn)行結(jié)果
程序執(zhí)行結(jié)果:
member function of A
global function with name:lvlv
B's useFunc
member function of A
global function with name:lvlv
In B's useFunc(),i=2
A's print
B's print:jf
舉例二
代碼來(lái)源C++中的覆蓋與隱藏(詳細(xì)講解)
class father
{
public:
void show1()
{
cout << "father::show1" << endl<< endl;
}
virtual void show2()
{
cout << "father::show2" << endl << endl;
}
};
class son:public father
{
public:
void show1()
{
cout << "son::show1" << endl<< endl;
}
virtual void show2()
{
cout << "son::show2" << endl << endl;
}
};
int main(){
//基類指針指向派生類對(duì)象的時(shí)候,基類指針可以直接調(diào)用派生類的覆蓋函數(shù),也可以通過(guò)::調(diào)用基類被覆蓋的虛函數(shù)
//而基類指針只能調(diào)用基類的被隱藏函數(shù),無(wú)法識(shí)別派生類中的隱藏函數(shù)
father f;
son s;
father *pf=&s;
son *ps=&s;
pf->show1(); //father::show1
pf->show2(); //son::show2
return 0;
}
- 因?yàn)閟how1是非virtual函數(shù),調(diào)用它的對(duì)象類型為靜態(tài)類型即父類(靜態(tài)聯(lián)編),所以調(diào)用的是父類的對(duì)象
- 但是show2為virtual函數(shù),調(diào)用它的對(duì)象類型為動(dòng)態(tài)類型即指針指向的類型(動(dòng)態(tài)聯(lián)編),所以調(diào)用的是子類的類型
3.2 虛函數(shù)和純虛函數(shù)
虛函數(shù)和純虛函數(shù)都是用于實(shí)現(xiàn)多態(tài)的機(jī)制,它們都支持動(dòng)態(tài)綁定的特性,但二者有著一些區(qū)別:
- 實(shí)現(xiàn)方式不同。虛函數(shù)需要在基類中有一個(gè)默認(rèn)的實(shí)現(xiàn),派生類可以選擇重載該函數(shù)或者使用默認(rèn)的實(shí)現(xiàn),而純虛函數(shù)沒(méi)有默認(rèn)的實(shí)現(xiàn),必須在派生類中進(jìn)行實(shí)現(xiàn)才能使用。
- 使用場(chǎng)景不同。虛函數(shù)應(yīng)被定義為默認(rèn)的實(shí)現(xiàn)和在派生類中重新實(shí)現(xiàn)的繼承函數(shù),它們讓繼承樹(shù)中不同的實(shí)現(xiàn)起到相同的作用,提高代碼重用性;而純虛函數(shù)則常被用作通用接口或抽象類,提供一種規(guī)范的實(shí)現(xiàn)方式,以確保子類中的實(shí)現(xiàn)得到規(guī)范化。
- 不同的初始化方式。派生類中的虛函數(shù)在默認(rèn)沒(méi)有被重載時(shí)和基類中的虛函數(shù)都指向同一個(gè)實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)允許在對(duì)象構(gòu)造時(shí)調(diào)用;而純虛函數(shù)不能在基類中被調(diào)用,因?yàn)槠錄](méi)有實(shí)現(xiàn),只作為規(guī)范的存在。如果執(zhí)行的代碼需要實(shí)現(xiàn)純虛函數(shù),這時(shí)只能從派生類構(gòu)造函數(shù)中調(diào)用。
- 對(duì)于純虛函數(shù),它們?cè)诨愔谐洚?dāng)抽象角色。在實(shí)際中,純虛函數(shù)常常適用于基類,因?yàn)榛惼鋵?shí)是一個(gè)不能被實(shí)例化的抽象類,它只包含一些接口,讓繼承它的派生類去實(shí)現(xiàn)這些接口;而虛函數(shù)則適用于一個(gè)可以被實(shí)例化的類,它通過(guò)繼承實(shí)現(xiàn)了多態(tài)性的特性。
- 純虛函數(shù)沒(méi)有具體的函數(shù)體,在虛表中的值為0,而具有函數(shù)體的虛函數(shù)則是函數(shù)的具體地址。
3.3 虛函數(shù)的代價(jià)
- 每一個(gè)帶有虛函數(shù)的類會(huì)產(chǎn)生一個(gè)虛函數(shù)表,用來(lái)存儲(chǔ)指向虛成員函數(shù)的指針
- 帶有虛函數(shù)的類的每一個(gè)對(duì)象會(huì)有一個(gè)指向虛表的指針,增加對(duì)象的空間大小
- 虛函數(shù)不能在世內(nèi)聯(lián)函數(shù)。因?yàn)閮?nèi)聯(lián)函數(shù)在編譯階段進(jìn)行替代,而虛函數(shù)在運(yùn)行階段才能確定用哪種函數(shù),虛函數(shù)不能是內(nèi)聯(lián)函數(shù)。
3.4 哪些函數(shù)不能是虛函數(shù)?
- 構(gòu)造函數(shù):派生類必須知道基類干了什么,才能進(jìn)行構(gòu)造;當(dāng)有虛函數(shù)的時(shí)候,每一個(gè)類有一個(gè)虛表,對(duì)象有虛表指針,虛表指針在構(gòu)造函數(shù)中初始化
- 內(nèi)聯(lián)函數(shù)
- 靜態(tài)函數(shù):靜態(tài)函數(shù)不屬于對(duì)象屬于類,沒(méi)有this指針,設(shè)置為虛函數(shù)沒(méi)有意義
- 友元函數(shù)、普通函數(shù):不屬于成員函數(shù),不能被繼承
3.5 靜態(tài)綁定和動(dòng)態(tài)綁定
- 靜態(tài)綁定:綁定的是靜態(tài)類型(對(duì)象在聲明時(shí)采用的類型,在編譯器確定),發(fā)生在編譯器
- 動(dòng)態(tài)綁定:綁定動(dòng)態(tài)類型(通常是指一個(gè)指針或引用目前所指對(duì)象的類型,是在運(yùn)行期決定的),所對(duì)應(yīng)的函數(shù)或?qū)傩砸蕾囉趯?duì)象的動(dòng)態(tài)類型,發(fā)生在運(yùn)行期。
建議:絕對(duì)不要重新定義繼承而來(lái)的非虛函數(shù),因?yàn)檫@樣重寫(xiě)的時(shí)候是沒(méi)有多態(tài)的,這樣會(huì)給程序留下不可預(yù)知的隱患和莫名其妙的bug,而且動(dòng)態(tài)綁定時(shí),要注意默認(rèn)參數(shù)的使用,當(dāng)缺省參數(shù)和virtual函數(shù)一起使用的時(shí)候要謹(jǐn)慎。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-434551.html
引用是否可以實(shí)現(xiàn)動(dòng)態(tài)綁定
引用在創(chuàng)建的時(shí)候必須初始化,在訪問(wèn)虛函數(shù)的時(shí)候,編譯器會(huì)根據(jù)綁定的對(duì)象類型決定要調(diào)用哪個(gè)函數(shù)(只能調(diào)用虛函數(shù))。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-434551.html
3.6 如何防止一個(gè)類被實(shí)例化?
- 將類定義為抽象基類或者將構(gòu)造函數(shù)聲明為private
- 不允許外部創(chuàng)建類對(duì)象,只能在類內(nèi)部創(chuàng)建對(duì)象(static)
到了這里,關(guān)于c++面向?qū)ο笾庋b、繼承、和多態(tài)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!