<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
繼承機制是物件導向程式設計為了提高程式碼複用率的一種手段,它可以保持原類特性的基礎上進行拓展,簡單來說繼承是類層次的複用。
接下來我們來看一個簡單的繼承
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; };
在上面這個類中繼承後父類別(Person)的成員都會變成子類的一部分。
格式:
class 子類:
public 父類別{ };
繼承基礎類別成員存取方式的變化
類成員/繼承方式 | public繼承 | protected繼承 | private繼承 |
---|---|---|---|
基礎類別的public成員 | 派生類的public成員 | 派生類的protected成員 | 派生類的private成員 |
基礎類別的protected成員 | 派生類的protected成員 | 派生類的protected成員 | 派生類的private成員 |
基礎類別的private成員 | 在派生類中不可見 | 在派生類中不可見 | 在派生類中不可見 |
派生類物件可以賦值給基礎類別的物件/指標/參照。這裡有一個形象的書法叫做切片或切割
基礎類別物件不能賦值給派生類物件
基礎類別的指標可以通過強制型別轉換賦值給派生類的指標。但是必須是基礎類別的指標是指向派生類物件時才是安全地。
在繼承體系中基礎類別和派生類都有獨立的作用域。
子類和父類別中有同名成員;子類成員將遮蔽父類別對同名成員的直接存取,這種情況叫做隱藏,也叫作重定義。如果要存取父類別的成員可以使用域作用限定符進行存取。
注意函數構成隱藏的話只需要函數名相同。
實際在繼承體系裡面最好不要定義同名的成員。
在這裡又把類與物件中學的六個預設成員函數拉出來了,那麼在繼承體系中這幾個成員函數是如何生成的呢?
派生類物件初始化先呼叫基礎類別構造再調派生類構造
派生類物件解構清理先呼叫派生類解構再調基礎類別的解構。
簡單的運用:
class Person { public: Person(const char* name="zhao") :_name(name) { cout<<"父構造"<<endl; } Person(const Person& p) :_name(p.name) { cout<<"父拷貝構造"<<endl; } Person& operator=(const Person& p) { cout<<"父賦值過載"<<endl; if(this!=&p) _name=p.name; return *this; } ~Person() { cout<<"父解構"<<endl; } protected: string _name; }; class Student:public Person { public: Student(const char* name,int num) :Person(name) ,_num(num) { cout<<"子構造"<<endl; } Student(const Student& s) :Person(s) ,_num(num) { cout<<"子拷貝構造"<<endl; } Student& operator=(const Student& s) { cout<<"子賦值過載"<<endl; if(this!=&s) { //小心這裡是隱藏 Person::operator=(s); _num=s._num; } return *this; } //需要注意在這塊~Student()和~Person()構成隱藏,這是由於多型的一些原因,任何類解構函式名都會被統一處理為destructor() ~Student() { cout<<"子解構"<<endl; //為了保證解構時,保持先子再父的後進先出的解構順序,子類解構函式完成後,會自動去呼叫父類別的解構函式。 } protected: int _num; };
友元關係不能繼承,也就是說基礎類別友元不是子類的友元。
基礎類別定義了static靜態成員,則整個繼承體系裡面只有一個這樣的成員,無論派生出多少個子類,都只有一個static成員範例。
class Person { public : Person () {++ _count ;} protected : string _name ; // 姓名 public : static int _count; // 統計人的個數。 }; int Person :: _count = 0; class Student : public Person { protected : int _stuNum ; // 學號 }; class Graduate : public Student { protected : string _seminarCourse ; // 研究科目 }; void TestPerson() { Student s1 ; Student s2 ; Student s3 ; Graduate s4 ; cout <<" 人數 :"<< Person ::_count << endl; Student ::_count = 0; cout <<" 人數 :"<< Person ::_count << endl; }
單繼承:一個子類只有一個直接父類別時稱這個繼承關係為單繼承
多繼承:一個子類有兩個或以上直接父類別時稱這個繼承關係為多繼承
菱形繼承:菱形繼承是多繼承的一種特殊情況。
菱形繼承的問題:從下面的物件成員模型構造,可以看出菱形繼承有資料冗餘和二義性的問題。在Assistant的物件中Person成員會有兩份。
虛擬繼承可以解決菱形繼承的二義性和資料冗餘的問題。如上面的繼承關係,在Student和Teacher的繼承Person時使用虛擬繼承,即可解決問題。需要注意的是,虛擬繼承不要在其他地方去使用
虛擬繼承解決資料冗餘和二義性的原理為了研究虛擬繼承原理,我們給出了一個簡化的菱形繼承繼承體系,再借助記憶體視窗觀察物件成員的模型。
虛繼承實現:
在腰部兩個繼承之前加上關鍵字vittul,實現虛繼承。
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; }
要探究虛繼承如何實現,需要借用VS的開發人員命令提示工具,在VS2019的工具->命令列->開發者命令提示中。cd到當前專案的目錄,輸入cl /d1reportSingleClassLayout"要檢視的類名" “檔名”,在這裡就是cl/d1reportSingleClassLayoutSheepTuo diamond_Inherit.cpp。可以看到當前類記憶體的結構。(編譯後才能檢視到記憶體分佈)
這個圖就是記憶體結構,可以看到,SheepTuo類中分別繼承了來自Sheep類的vbptr(虛基礎類別指標)和Tuo類的vbptr(虛基礎類別指標)。這個虛基礎類別指標指向的是一個虛基礎類別表,可以在圖中看到虛基礎類別表中第一項儲存的是vbptr與本類的偏移地址,也就是繼承過來的Sheep類中初始位置就是存放Sheep類的的vbptr,在這裡為0;第二項是本類的vbptr與虛基礎類別的公有成員之間的偏移量,也就是Sheep的vbptr和Animal類的age之間偏移為8,Tuo的vbptr和age之間偏移量為4。對於虛基礎類別的派生類,虛基礎類別的偏移量由實際型別決定,因此在執行時才可以確定虛基礎類別的地址。
指的注意的是,Sheep類中也是存放了一份age,在這裡還可以看到,Sheep和Tuo的Size都是8,因為除了繼承的age以外,還有Size為4的虛擬函式指標
因為class SheepTuo :public Sheep, public Tuo繼承的時候,把Sheep和Tuo的vbptr都繼承了,然後通過他們類距離虛基礎類別中的公共成員age的偏移量發現他們指向的是同一個age,所以就不會拷貝兩份,SheepTuo只保留一份age。至於虛繼承底層實現原理則與編譯器相關
很多人說C++語法複雜,其實多繼承就是一個體現。有了多繼承,就存在菱形繼承,有了菱形繼承就有菱形虛擬繼承,底層實現就很複雜。所以一般不建議設計出多繼承,一定不要設計出菱形繼承。否則在複雜度及效能上都有問題。
多繼承可以認為是C++的缺陷之一,很多後來的OO語言都沒有多繼承,如Java
繼承和組合
public繼承是一種is-a的關係。也就是說每個派生類物件都是一個基礎類別物件。
組合是一種has-a的關係。假設B組合了A,每個B物件中都有一個A物件。
優先使用物件組合,而不是類繼承 。
繼承允許你根據基礎類別的實現來定義派生類的實現。這種通過生成派生類的複用通常被稱為白箱複用(white-box reuse)。術語“白箱”是相對可視性而言:在繼承方式中,基礎類別的內部細節對子類可見 。繼承一定程度破壞了基礎類別的封裝,基礎類別的改變,對派生類有很大的影響。派生類和基礎類別間的依賴關係很強,耦合度高。
物件組合是類繼承之外的另一種複用選擇。新的更復雜的功能可以通過組裝或組合物件來獲得。物件組合要求被組合的物件具有良好定義的介面。這種複用風格被稱為黑箱複用(black-box reuse),因為物件的內部細節是不可見的。物件只以“黑箱”的形式出現。 組合類之間沒有很強的依賴關係,耦合度低。優先使用物件組合有助於你保持每個類被封裝。
實際儘量多去用組合。組合的耦合度低,程式碼維護性好。不過繼承也有用武之地的,有些關係就適合繼承那就用繼承,另外要實現多型,也必須要繼承。類之間的關係可以用繼承,可以用組合,就用組合
到此這篇關於C++詳細講解繼承與虛繼承實現的文章就介紹到這了,更多相關C++ 繼承內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45