這里接上面C++之多態(tài)(上篇)
成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供常德網(wǎng)站建設(shè)、常德做網(wǎng)站、常德網(wǎng)站設(shè)計(jì)、常德網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、常德企業(yè)網(wǎng)站模板建站服務(wù),10余年常德做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。本篇目錄從上面虛函數(shù)的分析中我們已經(jīng)知道了多態(tài)的原理,接下來我們從更深層次去探索多態(tài)。
class Person
{public:
virtual void BuyTicket()
{cout<< "買票-全價(jià)"<< endl;
}
virtual void Func1(){}
};
class Student :public Person
{public:
virtual void BuyTicket()
{cout<< "買票-半價(jià)"<< endl;
}
virtual void Func2(){}
};
int main()
{//同一個(gè)類型的對象共用一個(gè)虛表
Person p1;
Person p2;
//vs下,不管是否完成重寫,子類虛表跟父類虛表都不是同一個(gè)
Student s1;
Student s2;
return 0;
}
通過上圖我們發(fā)現(xiàn),同一個(gè)類型的對象共用同一個(gè)虛表,vs下,不管是否完成重寫,子類虛表跟父類虛表都不是同一個(gè),除此之外,我們發(fā)現(xiàn),vs的監(jiān)視窗口下,子類自己的虛函數(shù)Func2(), 它是在子類自己的虛函數(shù)表中的,但是vs的監(jiān)視窗口卻沒有顯示出來,下面我們用一段程序?qū)⑵湔故境鰜怼?br />虛表的本質(zhì)是一個(gè)函數(shù)指針數(shù)組
通過上圖我們發(fā)現(xiàn)對象虛表的地址在它對象的地址處的值取前4個(gè)字節(jié)即可。
class Person
{public:
virtual void BuyTicket()
{cout<< "Person::買票-全價(jià)"<< endl;
}
virtual void Func1()
{cout<< "Person::Func1()"<< endl;
}
};
class Student :public Person
{public:
virtual void BuyTicket()
{cout<< "Student::買票-半價(jià)"<< endl;
}
virtual void Func2()
{cout<< "Student::Func2()"<< endl;
}
};
typedef void(*VFPTR)();
//void PrintVFTable(VFPTR table[])//打印虛函數(shù)表
void PrintVFTable(VFPTR* table,size_t n)//打印虛函數(shù)表中的虛函數(shù)地址并且調(diào)用虛函數(shù)
{//for (size_t i = 0; table[i] != nullptr; ++i)
for (size_t i = 0; i< n; ++i)
{printf("vft[%d]:%p->", i, table[i]);
//table[i]();
VFPTR pf = table[i];//有點(diǎn)類似與強(qiáng)制類型轉(zhuǎn)換
pf();
}
cout<< endl;
}
int main()
{//同一個(gè)類型的對象共用一個(gè)虛表
Person p1;
Person p2;
//vs下,不管是否完成重寫,子類虛表跟父類虛表都不是同一個(gè)
Student s1;
Student s2;
//取對象頭部虛函數(shù)表指針傳遞過去
//這里的2,3是我們知道對象的虛函數(shù)的個(gè)數(shù)
PrintVFTable((VFPTR*)*(int*)&p1,2);
PrintVFTable((VFPTR*)*(int*)&s1,3);
return 0;
}
4.3 C++ 11 override和final從上面可以看出,C++對函數(shù)的重寫的要求比較嚴(yán)格,但是有些情況下由于疏忽,可能會導(dǎo)致函數(shù)名的字母次序?qū)懛炊鵁o法構(gòu)成重載,而這種錯(cuò)誤在編譯期間是不會報(bào)出來的,只有程序運(yùn)行時(shí)沒有得到預(yù)期結(jié)果才來debug會得不償失,因此:C++ 11提供了override和final這兩個(gè)關(guān)鍵字,可以用來幫助用戶檢測是否重寫。
1.final:修飾虛函數(shù),表示該虛函數(shù)不能被重寫
class Car
{public:
virtual void Drive()final{}
};
class Benz :public Car
{public:
virtual void Drive() //error
{cout<< "Benz-舒適"<< endl;
}
};
2.override:檢查派生類虛函數(shù)是否重寫基類某個(gè)虛函數(shù),如果沒有編譯報(bào)錯(cuò)
class Car
{public:
virtual void Drive(){}
};
class Benz :public Car
{public:
//檢查子類虛函數(shù)是否完成重寫
virtual void Drive()override
{cout<< "Benz-舒適"<< endl;
}
};
4.4 重載、重寫(覆蓋)、隱藏(重定義)的對比 (函數(shù)之間的關(guān)系)5.抽象類
5.1概念在虛函數(shù)的后面寫上 =0,則這個(gè)函數(shù)為純虛函數(shù),包含純虛函數(shù)的類叫做抽象類(也叫接口類),抽象類不能實(shí)例化出對象。派生類繼承后也不能實(shí)例化出對象,只有重寫純虛函數(shù),派生類才能實(shí)例化出對象。純虛函數(shù)規(guī)范了派生類必須重寫,另外純虛函數(shù)更體現(xiàn)出了接口繼承。
class Car
{public:
virtual void Drive() = 0;
};
class Benz :public Car
{public:
virtual void Drive()
{cout<< "Benz-舒適"<< endl;
}
};
class BwM:public Car
{public:
virtual void Drive()
{cout<< "BMW-操控"<< endl;
}
};
int main()
{//Car c;//抽象類不能實(shí)例化對象
//BwM b;
Car* ptr = new BwM;
ptr->Drive();//多態(tài)的體現(xiàn)
ptr = new Benz;
ptr->Drive();
return 0;
}
5.2接口繼承和實(shí)現(xiàn)繼承普通函數(shù)的繼承是一種實(shí)現(xiàn)繼承,派生類繼承了基類函數(shù),可以使用函數(shù),繼承的是函數(shù)的實(shí)現(xiàn)。虛函數(shù)的繼承是一種接口繼承,派生類繼承的是虛函數(shù)的接口,目的是為了重寫,達(dá)成多態(tài),繼承的是接口。所以如果不實(shí)現(xiàn)多態(tài),不要把函數(shù)定義成虛函數(shù)。
6.單繼承和多繼承關(guān)系的虛函數(shù)表需要注意的是在單繼承和多繼承關(guān)系中, 下面我們?nèi)リP(guān)注的是派生類對象的虛表模型,因?yàn)榛惖奶摫砟P颓懊嫖覀円呀?jīng)看過了,沒什么需要特別研究的。
class Base1
{public:
virtual void func1()
{cout<< "Base1::func1"<< endl;
}
virtual void func2()
{cout<< "Base1::func2"<< endl;
}
private:
int b1 = 1;
};
class Base2
{public:
virtual void func1()
{cout<< "Base2::func1"<< endl;
}
virtual void func2()
{cout<< "Base2::func2"<< endl;
}
private:
int b2 = 2;
};
class Derive :public Base1, public Base2
{public:
virtual void func1()
{cout<< "Derive::func1"<< endl;
}
virtual void func3()
{cout<< "Derive::func3"<< endl;
}
private:
int d = 3;
};
typedef void(*VFPTR)();
//void PrintVFTable(VFPTR table[])//打印虛函數(shù)表
void PrintVFTable(VFPTR* table,size_t n)//打印虛函數(shù)表中的虛函數(shù)地址并且調(diào)用虛函數(shù)
{//for (size_t i = 0; table[i] != nullptr; ++i)
for (size_t i = 0; i< n; ++i)
{printf("vft[%d]:%p->", i, table[i]);
table[i]();
VFPTR pf = table[i];//有點(diǎn)類似與強(qiáng)制類型轉(zhuǎn)換
pf();
}
cout<< endl;
}
int main()
{Derive d;
PrintVFTable((VFPTR*)*(int*)&d,3);//打印Base1虛函數(shù)表
PrintVFTable((VFPTR*)*(int*)((char*)&d + sizeof(Base1)), 2);//法一:打印Base2虛函數(shù)表
//法二:打印Base2虛函數(shù)表
Base2* ptr2 = &d;//切片得到Base2的地址
PrintVFTable((VFPTR*)(*(int*)ptr2), 2);
return 0;
}
更深層次的問題
觀察上圖我們發(fā)現(xiàn)Base1中的func1和Base2中的func1都被Derive進(jìn)行了重寫,它們的內(nèi)容是一樣的,應(yīng)該指向同一份函數(shù),但是它們的地址為什么不一樣呢?
下篇文章我們會揭曉!??!
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧