C++基础15-类和对象之多态
總結(jié):
1、在父類中申明虛函數(shù)時(shí),一般情況下在子類中也申明(便于讀代碼)
一、賦值兼容
賦值兼容規(guī)則是指在需要基類對(duì)象的任何地方都可以使用公有派生類的對(duì)象來替代。
賦值兼容是一種默認(rèn)行為,不需要任何的顯示的轉(zhuǎn)化步驟。
賦值兼容規(guī)則中所指的替代包括以下的情況:
???????? 派生類的對(duì)象可以賦值給基類對(duì)象。
????? ?? 派生類的對(duì)象可以初始化基類的引用。
?????? ? 派生類對(duì)象的地址可以賦給指向基類的指針。
在替代之后,派生類對(duì)象就可以作為基類的對(duì)象使用,但只能使用從基類繼承的成員。
二、多態(tài)
C++中所謂的多態(tài)(polymorphism)是指,由繼承而產(chǎn)生的相關(guān)的不同的類,其對(duì)象對(duì)同一消息會(huì)作出不同的響應(yīng)。
如果父類指針指向的是父類對(duì)象則調(diào)用父類中定義的函數(shù);
如果父類指針指向的是子類對(duì)象則調(diào)用子類中定義的重寫函數(shù);
1解決方案:
???? ? C++中通過virtual關(guān)鍵字對(duì)多態(tài)進(jìn)行支持
?????? 使用virtual聲明的函數(shù)被重寫后即可展現(xiàn)多態(tài)特性
將需要表現(xiàn)多態(tài)的父類中的函數(shù)聲明為虛函數(shù),即在原有函數(shù)前加virtual關(guān)鍵字,并在子類函數(shù)中對(duì)父類虛函數(shù)進(jìn)行重寫
2、多態(tài)成立的條件
?????? <1>、要有繼承
??????? <2>、要有虛函數(shù)重寫
??????? <3>、要有父類指針(引用)指向子類對(duì)象
3、多態(tài)的實(shí)現(xiàn)原理
????? 虛函數(shù)表和vptr指針
????? (1)當(dāng)類中聲明虛函數(shù)時(shí),編譯器會(huì)在類中生成一個(gè)虛函數(shù)表,
??????????????? 虛函數(shù)表是一個(gè)存儲(chǔ)類成員函數(shù)指針的數(shù)據(jù)結(jié)構(gòu);
?????????????? 虛函數(shù)表是由編譯器自動(dòng)生成與維護(hù)的;
?????????????? virtual成員函數(shù)會(huì)被編譯器放入虛函數(shù)表中;
??????? (2)存在虛函數(shù)時(shí),每個(gè)對(duì)象中都有一個(gè)指向虛函數(shù)表的指針(vptr指針)。
說明:
1. 通過虛函數(shù)表指針VPTR調(diào)用重寫函數(shù)是在程序運(yùn)行時(shí)進(jìn)行的,因此需要通過尋址操作才能確定真正應(yīng)該調(diào)用的函數(shù)。而普通成員函數(shù)是在編譯時(shí)就確定了調(diào)用的函數(shù)。在效率上,虛函數(shù)的效率要低很多。
2.出于效率考慮,沒有必要將所有成員函數(shù)都聲明為虛函數(shù).
3.C++編譯器,執(zhí)行run函數(shù),不需要區(qū)分是子類對(duì)象還是父類對(duì)象,而是直接通過p的VPTR指針?biāo)赶虻膶?duì)象函數(shù)執(zhí)行即可。
4、驗(yàn)證vptr指針的存在(從多個(gè)類繼承虛函數(shù)就有多個(gè)vptr指針)
???????? 兩個(gè)類:類A,類B,都只有數(shù)據(jù)成員int a,類A中有虛函數(shù),類B中沒有。計(jì)算sizeof(A) sizeof(B)
32位:sizeof(A) =8?? sizeof(B)=4
64位:sizeof(A) =16 ? sizeof(B)=4? (64位一個(gè)指針占8字節(jié),類的內(nèi)存遵從字節(jié)對(duì)齊。故對(duì)齊后為8+8=16)
5、vptr指針初始化
vptr指針時(shí)分步驟初始化
?????? 1、調(diào)用父類構(gòu)造函數(shù)在調(diào)用父類構(gòu)造器時(shí),會(huì)將vptr指針當(dāng)做父類來處理。此時(shí)會(huì)臨時(shí)指向父類的虛函數(shù)表
?????? 2、處理自己構(gòu)造函數(shù),此時(shí)將子類對(duì)象的空間變成子類對(duì)象處理,vptr指針就從指向父類的表 變成 指向子類的表
注:不要在構(gòu)造函數(shù)中處理業(yè)務(wù)。調(diào)用虛函數(shù)
6、父類指針步長(zhǎng)和子類指針步長(zhǎng)
父類指針步長(zhǎng)為 sizeof(父類)
子類指針步長(zhǎng)為 sizeof(子類)
注:當(dāng)父類指針指向子類對(duì)象時(shí),指針加加,為父類指針步長(zhǎng),寫一個(gè)可能并不是子類對(duì)象(父類所占空間小于等于子類空間,父類空間++,可能指針指向的并不是子類的開始地址)
驗(yàn)證vptr指針存在代碼示例:
#if 1 #include<iostream> //http://www.mamicode.com/info-detail-1394321.html using namespace std; class Parent1 { public:virtual void func() {cout << "Parent" << endl;} private:int a; }; class Parent2 { public:virtual void func() {cout << "Parent" << endl;}virtual void func(int a) {cout << "Parent" << endl;} private:int a; }; class Parent3 { public:void func() {cout << "Parent" << endl;} private:int a; }; class Parent4 { public:void func() {cout << "Parent" << endl;} private:int a; };void test01() {Parent1 p1;Parent2 p2;Parent3 p3;Parent4 p4;cout << "sizeof(parent1)" << sizeof(p1) << endl; //多出來的字節(jié)是vptr指針?biāo)加每臻gcout << "sizeof(parent2)" << sizeof(p2) << endl;cout << "sizeof(parent3)" << sizeof(p3) << endl;cout << "sizeof(parent4)" << sizeof(p4) << endl;} //字節(jié)對(duì)齊 /* sizeof(parent1)16 sizeof(parent2)16 sizeof(parent3)4 sizeof(parent4)4 */int main() {test01();return 0; } #endifvptr指針初始化代碼示例:
#if 1 #include<iostream> using namespace std; //vptr指針時(shí)分步驟初始化 //1、調(diào)用父類構(gòu)造函數(shù) //2、處理自己構(gòu)造函數(shù) //不要在構(gòu)造函數(shù)中處理業(yè)務(wù)。調(diào)用虛函數(shù) class Parent { public:Parent(int a) {cout << "Parent(int)..." << endl;this->a = a;print(); //調(diào)用父類print還是子類print?父類printfunc();}virtual void print() {cout << "Parent::print()...a="<<this->a << endl;}void func() {cout << "Parent:func" << endl;} private:int a; }; class Child :public Parent { public:Child(int a, int b) :Parent(a) //在調(diào)用父類構(gòu)造器時(shí),會(huì)將vptr指針當(dāng)做父類來處理//此時(shí)會(huì)臨時(shí)指向父類的虛函數(shù)表{//將子類對(duì)象的空間變成子類對(duì)象處理//vptr指針就從指向父類的表 變成 指向子類的表cout << "Child(int,int)..." << endl;this->b = b;print();func();Parent::func();}virtual void print() {cout << "Child::print()...b=" <<this->b<< endl;}void func() {cout << "Child:func" << endl;} private:int b; }; void test01() {Parent *pp = new Child(10, 20);pp->print(); //發(fā)生多態(tài)delete pp; } /* Parent(int)... Parent::print()...a=10 Parent:func Child(int,int)... Child::print()...b=20 Child:func Parent:func Child::print()...b=20 */ int main() {test01();return 0; } #endif父類指針步長(zhǎng)和子類指針步長(zhǎng)示例:
#if 1 #include<iostream> using namespace std; #if 0 class Parent { public:Parent(int a) {cout << "Parent(int)..." << endl;this->a = a;}virtual void print() {cout << "Parent::print()...a=" << this->a << endl;}public:int a; }; class Child :public Parent { public:Child(int a) :Parent(a){cout << "Child(int,int)..." << endl;//print();}virtual void print() {cout << "Child::print()...b=" << this->a << endl;} }; void test01() {Child array[] = { Child(0),Child(1),Child(2) };for (int i = 0; i < 3; i++){array[i].print();} } void test02() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];pp->print();Child *cp = &array[0];cp->print();pp++; //pp+sizeof(Parent)cp++; //cp+sizeof(Child)pp->print();cp->print(); } void test03() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];int i = 0;for (pp = &array[0],i = 0; i < 3; i++, pp++) {pp->print();} } void test04() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];int i = 0;for (pp = &array[0], i = 0; i < 3; i++, pp++) {pp->print();} } #endif //在上面的代碼基礎(chǔ)上,在Child加int 運(yùn)行報(bào)錯(cuò) 當(dāng)pp++時(shí)報(bào)錯(cuò) //因?yàn)? pp++; //pp+sizeof(Parent) // cp++; //cp+sizeof(Child) //兩個(gè)步長(zhǎng)不一樣。 class Parent { public:Parent(int a) {cout << "Parent(int)..." << endl;this->a = a;}virtual void print() {cout << "Parent::print()...a=" << this->a << endl;}public:int a; }; class Child :public Parent { public:Child(int a) :Parent(a) {cout << "Child(int,int)..." << endl;//print();}virtual void print() {cout << "Child::print()...b=" << this->a << endl;} public:int b; }; void test01() {Child array[] = { Child(0),Child(1),Child(2) };for (int i = 0; i < 3; i++){array[i].print();} } /* Parent(int)... Child(int,int)... Parent(int)... Child(int,int)... Parent(int)... Child(int,int)... Child::print()...b=0 Child::print()...b=1 Child::print()...b=2 */ void test02() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];pp->print();Child *cp = &array[0];cp->print();pp++; //pp+sizeof(Parent)cp++; //cp+sizeof(Child)pp->print();cp->print(); } /* 程序崩潰 Parent(int)... Child(int,int)... Parent(int)... Child(int,int)... Parent(int)... Child(int,int)... Child::print()...b=0 Child::print()...b=0 */ void test03() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];int i = 0;for (pp = &array[0], i = 0; i < 3; i++, pp++) {pp->print();} } /* 程序崩潰 Parent(int)... Child(int,int)... Parent(int)... Child(int,int)... Parent(int)... Child(int,int)... Child::print()...b=0 */ void test04() {Child array[] = { Child(0),Child(1),Child(2) };Child *cp = &array[0];int i = 0;for (cp = &array[0], i = 0; i < 3; i++, cp++) {cp->print();} } /* Parent(int)... Child(int,int)... Parent(int)... Child(int,int)... Parent(int)... Child(int,int)... Child::print()...b=0 Child::print()...b=1 Child::print()...b=2 */ int main() {//test01();//test02();test03();//test04();return 0; } #endif代碼示例:
1、多態(tài)
#if 1 #include<iostream> #include<string> using namespace std; #if 0 class Hero { public:string kongfu; public:Hero(string kongfu) {this->kongfu = kongfu;}void fight() {cout << "英雄使出了" << this->kongfu << endl;} }; class Yuebuqun :public Hero { public:Yuebuqun(string kongfu):Hero(kongfu){}void fight() {cout << "Yuebuqun使出了" << this->kongfu << endl;} };class Linghuchong :public Yuebuqun { public:Linghuchong(string kongfu) :Yuebuqun(kongfu){}void fight(){cout << "Linghuchong使出了" << this->kongfu << endl;} }; void fight(Hero *hero) {cout << "調(diào)用打人的方法" << endl;hero->fight();//希望傳遞進(jìn)來的如果是子類,調(diào)用子類的fight//如果傳遞進(jìn)來的是父類, 調(diào)用父類的fight//這種行為就是 多態(tài)行為。 } void test01() {Hero h1("高招");h1.fight();Yuebuqun y1("y高招");y1.fight();Linghuchong l1("l高招");l1.fight(); } /* 英雄使出了高招 Yuebuqun使出了y高招 Linghuchong使出了l高招 */ void test02() {Yuebuqun *y1 = new Yuebuqun("y高招");y1->fight();Linghuchong *l1 = new Linghuchong("l高招");l1->fight();delete y1;delete l1; } /* Yuebuqun使出了y高招 Linghuchong使出了l高招 */ void test03() {Yuebuqun *y1 = new Yuebuqun("y高招");Linghuchong *l1 = new Linghuchong("l高招");fight(y1);fight(l1);delete y1;delete l1; } /* 調(diào)用打人的方法 英雄使出了y高招 調(diào)用打人的方法 英雄使出了l高招 */int main() {test01();//test02();//test03();return 0; } #endif class Hero { public:string kongfu; public:Hero(string kongfu) {this->kongfu = kongfu;}virtual void fight() {cout << "英雄使出了" << this->kongfu << endl;} }; class Yuebuqun :public Hero { public:Yuebuqun(string kongfu) :Hero(kongfu) {}void fight() {cout << "Yuebuqun使出了" << this->kongfu << endl;} };class Linghuchong :public Yuebuqun { public:Linghuchong(string kongfu) :Yuebuqun(kongfu) {}void fight() {cout << "Linghuchong使出了" << this->kongfu << endl;} }; void fight(Hero *hero) {cout << "調(diào)用打人的方法" << endl;hero->fight();//當(dāng)把父類方法聲明為virtual時(shí)。可以實(shí)現(xiàn)傳進(jìn)來誰,調(diào)用誰的方法//希望傳遞進(jìn)來的如果是子類,調(diào)用子類的fight//如果傳遞進(jìn)來的是父類, 調(diào)用父類的fight//這種行為就是 多態(tài)行為。 }void test04() {Yuebuqun *y1 = new Yuebuqun("y高招");Linghuchong *l1 = new Linghuchong("l高招");Hero *h1 = new Hero("h高招");fight(y1); fight(l1);fight(h1);delete y1;delete l1;delete h1; } /* 調(diào)用打人的方法 Yuebuqun使出了y高招 調(diào)用打人的方法 Linghuchong使出了l高招 調(diào)用打人的方法 英雄使出了h高招 */ int main() {test04(); }#endif2、多態(tài)意義
#if 1 #include<iostream> using namespace std; //英雄類 class Hero { public:Hero() {cout << "dd" << endl;}virtual int getAd() {return 10;} }; class AdvHero:public Hero { public:virtual int getAd() { //virtual寫不寫都o(jì)k,寫上能加強(qiáng)代碼的可讀性return 1001;} }; //怪獸類 class Monster { public:int getAd() {return 1000;} };//戰(zhàn)斗方法 void playerFight(Hero *hp, Monster *mp) {if (hp->getAd() > mp->getAd()) { //hp->getAd()發(fā)生了多態(tài)cout << "英雄勝利,怪獸被打死了" << endl;}elsecout << "英雄死亡,怪獸贏了" << endl; }//2020年 //拓展新東西 不改變之前代碼 //多態(tài)意義 架構(gòu)意義 class BugHero :public Hero { public:virtual int getAd() {cout << "調(diào)用BugHero方法" << endl;return 666666;} }; void test01() {Hero h;Monster m;playerFight(&h, &m);AdvHero advH;playerFight(&advH, &m);BugHero b;playerFight(&b, &m); }int main() {test01();Hero *h1[6]; //不調(diào)用析構(gòu)函數(shù) 沒有具體指向//int array[][2];cout << "dddddddsfad" << endl;Hero h[5]; //調(diào)用5次構(gòu)造函數(shù) } /* dd 英雄死亡,怪獸贏了 dd 英雄勝利,怪獸被打死了 dd 調(diào)用BugHero方法 英雄勝利,怪獸被打死了 dddddddsfad dd dd dd dd dd */ #endif多態(tài)原因:
#if 0 #include<iostream> using namespace std;class Parent { public:Parent(int a) {this->a = a;}virtual void func(int a) {cout << "Parent:func(int).." << endl;}void PrintP(){} private:int a; }; class Child :public Parent { public:Child(int a, int b) :Parent(b) {this->b = b;}virtual void func(int b) {cout << "Child::func(int)" << endl;}void func(int a, int b) {cout << "Child:func(int a,int b)" << endl;}void printC(){} private:int b; };void myFunc(Parent *p) {p->func(10); } void test01() {Parent *pp = new Parent(10);Parent *cp = new Child(100,200); //父類指針指向子類對(duì)象myFunc(pp);cout << "------" << endl;myFunc(cp); cp->PrintP(); //如果調(diào)用普通函數(shù),編譯器根本不會(huì)查找虛函數(shù)表//只有你調(diào)用的是虛函數(shù),才會(huì)去查找虛函數(shù)表 } int main() {test01();return 0; } #endif?
總結(jié)
以上是生活随笔為你收集整理的C++基础15-类和对象之多态的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务端和客户端测试连通ip设置记录
- 下一篇: mysql 默认事务隔离级别_上个厕所的