您好,登錄后才能下訂單哦!
這篇文章主要介紹“C++繼承與虛繼承怎么實(shí)現(xiàn)”,在日常操作中,相信很多人在C++繼承與虛繼承怎么實(shí)現(xiàn)問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”C++繼承與虛繼承怎么實(shí)現(xiàn)”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
繼承機(jī)制是面向?qū)ο蟪绦蛟O(shè)計(jì)為了提高代碼復(fù)用率的一種手段,它可以保持原類特性的基礎(chǔ)上進(jìn)行拓展,簡單來說繼承是類層次的復(fù)用。
接下來我們來看一個(gè)簡單的繼承
class Person { public: void Print() { cout<<"name:"<<_name<<endl; cout<<"age:"<<_age<<endl; } protected: string _name="zhao"; int _age=18; }; class Student : public Person { protected: int _stuid; }; class Teacher :public Person { protected: int _jobid; };
在上面這個(gè)類中繼承后父類(Person)的成員都會(huì)變成子類的一部分。
格式:
class 子類:
public 父類{ };
繼承基類成員訪問方式的變化
類成員/繼承方式 | public繼承 | protected繼承 | private繼承 |
---|---|---|---|
基類的public成員 | 派生類的public成員 | 派生類的protected成員 | 派生類的private成員 |
基類的protected成員 | 派生類的protected成員 | 派生類的protected成員 | 派生類的private成員 |
基類的private成員 | 在派生類中不可見 | 在派生類中不可見 | 在派生類中不可見 |
基類private成員在派生類中無論以什么方式繼承都是不可見的。這里的不可見是指基類的私有成員還是被繼承到了派生類對象中,但是語法上限制派生類對象不管在類里面還是類外面都不能去訪問它。
基類private成員在派生類中是不能被訪問,如果基類成員不想在類外直接被訪問,但需要在派生類中能訪問,就定義為protected??梢钥闯霰Wo(hù)成員限定符是因繼承才出現(xiàn)的。
實(shí)際上面的表格我們進(jìn)行一下總結(jié)會(huì)發(fā)現(xiàn),基類的私有成員在子類都是不可見?;惖钠渌蓡T在子類的訪問方式 == Min(成員在基類的訪問限定符,繼承方式),public > protected > private。
使用關(guān)鍵字class時(shí)默認(rèn)的繼承方式是private,使用struct時(shí)默認(rèn)的繼承方式是public,不過最好顯示的寫出繼承方式。
在實(shí)際運(yùn)用中一般使用都是public繼承,幾乎很少使用protetced/private繼承,也不提倡使用protetced/private繼承,因?yàn)閜rotetced/private繼承下來的成員都只能在派生類的類里面使用,實(shí)際中擴(kuò)展維護(hù)性不強(qiáng)。
派生類對象可以賦值給基類的對象/指針/引用。這里有一個(gè)形象的書法叫做切片或切割
基類對象不能賦值給派生類對象
基類的指針可以通過強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針。但是必須是基類的指針是指向派生類對象時(shí)才是安全地。
在繼承體系中基類和派生類都有獨(dú)立的作用域。
子類和父類中有同名成員;子類成員將屏蔽父類對同名成員的直接訪問,這種情況叫做隱藏,也叫作重定義。如果要訪問父類的成員可以使用域作用限定符進(jìn)行訪問。
注意函數(shù)構(gòu)成隱藏的話只需要函數(shù)名相同。
實(shí)際在繼承體系里面最好不要定義同名的成員。
在這里又把類與對象中學(xué)的六個(gè)默認(rèn)成員函數(shù)拉出來了,那么在繼承體系中這幾個(gè)成員函數(shù)是如何生成的呢?
構(gòu)造函數(shù):派生類的構(gòu)造函數(shù)必須基類的構(gòu)造函數(shù)初始化基類的那部分成員。如果基類沒有默認(rèn)的構(gòu)造函數(shù),則必須在派生類構(gòu)造函數(shù)的初始化列表階段顯示調(diào)用。
拷貝構(gòu)造函數(shù):派生類的拷貝構(gòu)造函數(shù)必須調(diào)用基類的拷貝構(gòu)造完成基類的拷貝初始化。
賦值重載:派生類operator=必須要調(diào)用基類的operator=完成基類的賦值。
析構(gòu)函數(shù):派生類的析構(gòu)函數(shù)會(huì)在被調(diào)用完成后自動(dòng)調(diào)用基類的析構(gòu)函數(shù)清理基類成員。因?yàn)檫@樣才能保證派生類對象先清理派生類成員再清理基類成員的順序。
派生類對象初始化先調(diào)用基類構(gòu)造再調(diào)派生類構(gòu)造
派生類對象析構(gòu)清理先調(diào)用派生類析構(gòu)再調(diào)基類的析構(gòu)。
簡單的運(yùn)用:
class Person { public: Person(const char* name="zhao") :_name(name) { cout<<"父構(gòu)造"<<endl; } Person(const Person& p) :_name(p.name) { cout<<"父拷貝構(gòu)造"<<endl; } Person& operator=(const Person& p) { cout<<"父賦值重載"<<endl; if(this!=&p) _name=p.name; return *this; } ~Person() { cout<<"父析構(gòu)"<<endl; } protected: string _name; }; class Student:public Person { public: Student(const char* name,int num) :Person(name) ,_num(num) { cout<<"子構(gòu)造"<<endl; } Student(const Student& s) :Person(s) ,_num(num) { cout<<"子拷貝構(gòu)造"<<endl; } Student& operator=(const Student& s) { cout<<"子賦值重載"<<endl; if(this!=&s) { //小心這里是隱藏 Person::operator=(s); _num=s._num; } return *this; } //需要注意在這塊~Student()和~Person()構(gòu)成隱藏,這是由于多態(tài)的一些原因,任何類析構(gòu)函數(shù)名都會(huì)被統(tǒng)一處理為destructor() ~Student() { cout<<"子析構(gòu)"<<endl; //為了保證析構(gòu)時(shí),保持先子再父的后進(jìn)先出的析構(gòu)順序,子類析構(gòu)函數(shù)完成后,會(huì)自動(dòng)去調(diào)用父類的析構(gòu)函數(shù)。 } protected: int _num; };
友元關(guān)系不能繼承,也就是說基類友元不是子類的友元。
基類定義了static靜態(tài)成員,則整個(gè)繼承體系里面只有一個(gè)這樣的成員,無論派生出多少個(gè)子類,都只有一個(gè)static成員實(shí)例。
class Person { public : Person () {++ _count ;} protected : string _name ; // 姓名 public : static int _count; // 統(tǒng)計(jì)人的個(gè)數(shù)。 }; int Person :: _count = 0; class Student : public Person { protected : int _stuNum ; // 學(xué)號(hào) }; class Graduate : public Student { protected : string _seminarCourse ; // 研究科目 }; void TestPerson() { Student s1 ; Student s2 ; Student s3 ; Graduate s4 ; cout <<" 人數(shù) :"<< Person ::_count << endl; Student ::_count = 0; cout <<" 人數(shù) :"<< Person ::_count << endl; }
單繼承:一個(gè)子類只有一個(gè)直接父類時(shí)稱這個(gè)繼承關(guān)系為單繼承
多繼承:一個(gè)子類有兩個(gè)或以上直接父類時(shí)稱這個(gè)繼承關(guān)系為多繼承
菱形繼承:菱形繼承是多繼承的一種特殊情況。
菱形繼承的問題:從下面的對象成員模型構(gòu)造,可以看出菱形繼承有數(shù)據(jù)冗余和二義性的問題。在Assistant的對象中Person成員會(huì)有兩份。
虛擬繼承可以解決菱形繼承的二義性和數(shù)據(jù)冗余的問題。如上面的繼承關(guān)系,在Student和Teacher的繼承Person時(shí)使用虛擬繼承,即可解決問題。需要注意的是,虛擬繼承不要在其他地方去使用
虛擬繼承解決數(shù)據(jù)冗余和二義性的原理為了研究虛擬繼承原理,我們給出了一個(gè)簡化的菱形繼承繼承體系,再借助內(nèi)存窗口觀察對象成員的模型。
虛繼承實(shí)現(xiàn):
在腰部兩個(gè)繼承之前加上關(guān)鍵字vittul,實(shí)現(xiàn)虛繼承。
class Animal{ public: int _a; } class Tuo:virtual public Animal { public: int _b; } class Sheep:virtual public Animal { public: int _c; } class SheepTuo:public B ,public C { public: int _b; }
要探究虛繼承如何實(shí)現(xiàn),需要借用VS的開發(fā)人員命令提示工具,在VS2019的工具->命令行->開發(fā)者命令提示中。cd到當(dāng)前項(xiàng)目的目錄,輸入cl /d1reportSingleClassLayout"要查看的類名" “文件名”,在這里就是cl/d1reportSingleClassLayoutSheepTuo diamond_Inherit.cpp??梢钥吹疆?dāng)前類內(nèi)存的結(jié)構(gòu)。(編譯后才能查看到內(nèi)存分布)
這個(gè)圖就是內(nèi)存結(jié)構(gòu),可以看到,SheepTuo類中分別繼承了來自Sheep類的vbptr(虛基類指針)和Tuo類的vbptr(虛基類指針)。這個(gè)虛基類指針指向的是一個(gè)虛基類表,可以在圖中看到虛基類表中第一項(xiàng)存儲(chǔ)的是vbptr與本類的偏移地址,也就是繼承過來的Sheep類中初始位置就是存放Sheep類的的vbptr,在這里為0;第二項(xiàng)是本類的vbptr與虛基類的公有成員之間的偏移量,也就是Sheep的vbptr和Animal類的age之間偏移為8,Tuo的vbptr和age之間偏移量為4。對于虛基類的派生類,虛基類的偏移量由實(shí)際類型決定,因此在運(yùn)行時(shí)才可以確定虛基類的地址。
指的注意的是,Sheep類中也是存放了一份age,在這里還可以看到,Sheep和Tuo的Size都是8,因?yàn)槌死^承的age以外,還有Size為4的虛函數(shù)指針
因?yàn)閏lass SheepTuo :public Sheep, public Tuo繼承的時(shí)候,把Sheep和Tuo的vbptr都繼承了,然后通過他們類距離虛基類中的公共成員age的偏移量發(fā)現(xiàn)他們指向的是同一個(gè)age,所以就不會(huì)拷貝兩份,SheepTuo只保留一份age。至于虛繼承底層實(shí)現(xiàn)原理則與編譯器相關(guān)。
到此,關(guān)于“C++繼承與虛繼承怎么實(shí)現(xiàn)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。