日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

第九天2017/04/18(3、重载/覆盖 PK 重写/重定义、父类子类混搭风、抽象类)

發(fā)布時(shí)間:2025/3/21 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第九天2017/04/18(3、重载/覆盖 PK 重写/重定义、父类子类混搭风、抽象类) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1、重載/覆蓋 PK 重寫/重定義 【預(yù)備知識(shí)】 函數(shù)重載必須在同一個(gè)類中發(fā)生子類無(wú)法重載父類的函數(shù),父類同名的函數(shù)將會(huì)被名稱覆蓋重載是在編譯期間根據(jù)參數(shù)類型和個(gè)數(shù)決定函數(shù)調(diào)用重載只放在同一個(gè)類之中,在編譯期間就確定 函數(shù)重寫必須發(fā)生在父類與子類之間父類與子類中的函數(shù)必須有完全相同的函數(shù)原型使用virtual關(guān)鍵字聲明后能夠產(chǎn)生多態(tài)(如果沒(méi)有virtual,那叫重定義)多態(tài)是運(yùn)行期間根據(jù)具體對(duì)象的類型決定函數(shù)調(diào)用重寫發(fā)生在父子類之間, 覆蓋父類和子類中有“同名”的函數(shù),子類的函數(shù)會(huì)把父類的函數(shù)隱藏起來(lái) 重定義(是一種特殊的覆蓋)父類和子類中有“相同函數(shù)原型”的函數(shù),子類的函數(shù)會(huì)把父類的函數(shù)隱藏起來(lái) 1、重載/覆蓋 PK 重寫/重定義重載:在“同一個(gè)類”中,函數(shù)名相同、函數(shù)原型不同,此時(shí)發(fā)生重載覆蓋:無(wú)virtual關(guān)鍵字,在父類、子類中,函數(shù)名相同、函數(shù)原型不同,此時(shí)在子類中的函數(shù)隱藏了父類中的函數(shù)。 ---------------------------------------------------------------------------------------------重寫:有virtual關(guān)鍵字,在父類、子類中,函數(shù)名、函數(shù)原型都相同,此時(shí)發(fā)生重寫重定義:無(wú)virtual關(guān)鍵字,在父類、子類中,函數(shù)名、函數(shù)原型都相同,此時(shí)在子類中的函數(shù)隱藏了父類中的函數(shù),類似于“覆蓋”。#include <iostream> using namespace std; class B { public:void f() { }virtual void f(int i) { } void f(int i,int j) { } }; class D:public B { public://子類中沒(méi)有void f()函數(shù)void f(int i) { } //重寫void f(int i,int j) { } //發(fā)生名稱覆蓋void f(int i,int j,int k) { } //發(fā)生名稱覆蓋 }; void g(B& b) {b.f(1); }void main() {D d; /***************************************************************************/ //【重點(diǎn)】 //疑問(wèn):為什么子類對(duì)象不能調(diào)用父類中的f()函數(shù)? //答:因?yàn)樽宇愔杏泻瘮?shù)名為f的函數(shù),有由于子類的函數(shù)不會(huì)重載父類的函數(shù),所以 //子類中的f函數(shù)會(huì)把父類中的無(wú)參的f()函數(shù)給覆蓋,因此直接d.f()會(huì)發(fā)生編譯錯(cuò)誤!//d.f();//error: 沒(méi)有重載函數(shù)接受0個(gè)參數(shù)的f() //疑問(wèn):如果我就是想用子類對(duì)象去調(diào)用父類中的f()函數(shù),應(yīng)該怎么做? //答:加上作用域符B::,此時(shí)d對(duì)象就會(huì)調(diào)用父類B中的f()函數(shù)。d.B::f(); // //【結(jié)論】子類中的f()函數(shù)不會(huì)重載父類中的f()函數(shù),父類同名的函數(shù)將被覆蓋!重載只發(fā)生在同一個(gè)類中! /***************************************************************************/D dd; //覆蓋//dd.f(); //編譯失敗,因?yàn)闊o(wú)virtual發(fā)生同名覆蓋dd.B::f(); //作用域B::,調(diào)用子類中的void f() //覆蓋、重定義dd.B::f(1,2);//作用域B::,調(diào)用子類中的void f(int i,int j)dd.f(1,2); //覆蓋:調(diào)用子類中的void f(int i,int j) //多態(tài)B &b = d;b.f(1); //多態(tài):調(diào)用子類中的void f(int i)b.B::f(1); //作用域B::,調(diào)用父類中的void f(int i) //重載 } ---------------------------------------------------------------------- 2、父類對(duì)象、子類對(duì)象混搭風(fēng)【該模塊中,隱藏了一個(gè)天大的Bug:“P++步長(zhǎng)”】本質(zhì):由于步長(zhǎng)的影響 【結(jié)論】不要用父類指針p指向子類對(duì)象的數(shù)組,通過(guò)p++,去遍歷這個(gè)子類對(duì)象的數(shù)組。 同理:也不要用子類指針p指向父類對(duì)象的數(shù)組,通過(guò)p++,去遍歷這個(gè)父類對(duì)象的數(shù)組 【為什么?】因?yàn)楦割愔羔榩指向了子類對(duì)象的數(shù)組,在進(jìn)行p++的時(shí)候, p增加的步長(zhǎng)是sizeof(父類),但是由于sizeof(父類)不一定等于sizeof(子類), 因此p在進(jìn)行加1后,指向的位置不一定是下一個(gè)子類對(duì)象的首地址。當(dāng)p指向的位置 不是下一個(gè)子類對(duì)象的首地址的時(shí)候,如果進(jìn)行訪問(wèn)子類對(duì)象中的成員,程序必然會(huì)發(fā)生 崩潰。 //程序案例 #include <iostream> using namespace std; class A { public:virtual void f(){cout<<"A:f()"<<endl;} }; class B:public A { public:int i; //為什么在子類中加上一個(gè)屬性i,程序就會(huì)運(yùn)行崩潰:因?yàn)榧恿艘粋€(gè)變量,對(duì)步長(zhǎng)有影響 public:B(int i=0,int j=0){}virtual void f(){cout<<"B:f()"<<endl;} };void howToF(A* pBase) {pBase->f(); }int main() {A *p = NULL;B *q = NULL;B c[3] = {B(1,1),B(1,1),B(1,1)};p = c; //父類指針p指向由子類對(duì)象構(gòu)成的數(shù)組q = c; //子類指針q指向由子類對(duì)象構(gòu)成的數(shù)組for(int i=0;i<3;i++){//p->f(); //因?yàn)椴介L(zhǎng)的原因,訪問(wèn)成員時(shí),會(huì)發(fā)生程序崩潰q->f();p++; //p++增加的步長(zhǎng)是sizeof(父類),而不是sizeof(子類)q++;}for(int i=0;i<3;i++){howToF(&c[i]); //形參:子類對(duì)象的地址,實(shí)參:父類指針//解釋:此處運(yùn)行正確,為什么? //此處沒(méi)有用p++形式,而是用了下標(biāo)操作c[i],避開(kāi)了p++的步長(zhǎng)不一致導(dǎo)致的程序崩潰。}return 0; } ---------------------------------------------------------------------- 3、抽象類 #include <iostream> using namespace std; class A { public:virtual void ff() = 0; };//A g1(); //不允許使用返回抽象類 "A" 的函數(shù) A& g21(); //允許使用返回抽象類引用類型 "A&" 的函數(shù) A& g22(A&); A& g23(A*); A* g31(); //允許使用返回抽象類指針類型 "A*" 的函數(shù) A* g32(A*); A* g32(A&);//void f1(A a);//不允許使用抽象類類型 "A" 的參數(shù) void f2(A &a);//允許使用抽象類引用類型 "A&" 的參數(shù) void f2(A *a);//允許使用抽象類指針類型 "A*" 的參數(shù)class B:public A { public:void ff() { } }; int main() {//A a1;//不能實(shí)例化抽象類//A a2 = new A;//不能實(shí)例化抽象類A *a4 = new B; //可以用抽象類的指針指向抽象類的子類對(duì)象B b1;A &a3 = b1;//可以用抽象類的引用指向抽象類的子類對(duì)象 }4、多重繼承與抽象類--->實(shí)現(xiàn)多繼承接口 【知識(shí)復(fù)習(xí)】 #include <iostream> using namespace std; class A1 { public:virtual void ff() = 0; //純虛函數(shù)void gg() { cout<<"普通函數(shù)"<<endl; } //普通函數(shù)在A1中實(shí)現(xiàn) }; class A2 { public:virtual void ff() = 0; //純虛函數(shù)void gg() { cout<<"普通函數(shù)"<<endl; }//普通函數(shù)在A2中實(shí)現(xiàn) }; class B:public A1,public A2//B多重繼承A1、A2時(shí) { public:virtual void ff() //純虛函數(shù)ff在B中實(shí)現(xiàn){ cout<<"純虛函數(shù)"<<endl; }}; int main() {B b; //b訪問(wèn)純虛函數(shù)ff,不會(huì)發(fā)生二義性b.ff(); //b訪問(wèn)普通函數(shù)gg,會(huì)發(fā)生二義性//b.gg(); //編譯失敗 //防止二義性,應(yīng)該加上作用域標(biāo)識(shí)符A1::、A2::b.A1::gg();b.A2::gg(); } 【知識(shí)引出】 疑問(wèn):多重繼承會(huì)發(fā)生二義性,但是多重繼承在C++中有什么作用呢? 答:在項(xiàng)目開(kāi)發(fā)過(guò)程中,多重繼承的作用主要是:多重繼承多個(gè)抽象類(接口),這樣會(huì)有效的避免二義性。多繼承接口案例 #include <iostream> using namespace std; class A { public:virtual void ff(){ cout<<"ff:A"<<endl; } };class interface1 //抽象類接口1 { public:virtual void gg1() = 0; }; class interface2 //抽象類接口2 { public:virtual void gg2() = 0; }; class B:public A,public interface1,public interface2 { //B類繼承了類A、接口interface1、接口interface2 public:void gg1() { cout<<"gg1"<<endl; }void gg2() { cout<<"gg2"<<endl; }void kk(){ cout<<"KK"<<endl; } }; int main() {B b;b.ff();b.gg1();b.gg2();b.kk(); }

總結(jié)

以上是生活随笔為你收集整理的第九天2017/04/18(3、重载/覆盖 PK 重写/重定义、父类子类混搭风、抽象类)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。