C++深入探究繼承的概念與使用
1、概念及定義
1.1 概念
繼承主要的工作就是-----共性抽取
具體地講:
①繼承機(jī)制是面向?qū)ο蟪绦蛟O(shè)計(jì)使代碼可以復(fù)用的最重要的手段;
②允許程序員在保持原有類特性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加功能。這樣實(shí)現(xiàn)的類稱為派生類/子類。基于實(shí)現(xiàn)該類的原有類稱為基類/父類
③繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計(jì)的層次結(jié)構(gòu),體現(xiàn)了由簡單到復(fù)雜的認(rèn)知過程。(比如:animal—>dog---->kinds of dogs)
④繼承是類層次設(shè)計(jì)的復(fù)用
1.2 定義
定義方式:class 派生類:繼承方式 基類
繼承方式可以是 public、protected、private
三種,他們在繼承基類時,所具有特性以及表現(xiàn)出的結(jié)果也有所不同,具體如下:
以public的方式繼承基類
結(jié)論:
在public的繼承方式下:
①父類中的成員變量的訪問權(quán)限,到子類中不會發(fā)生改變
②父類中的私有訪問權(quán)限的變量在子類中不可見(不能直接被訪問)
問題:類在設(shè)計(jì)的時候,訪問權(quán)限應(yīng)該如何選擇?
應(yīng)該遵循以下3點(diǎn)原則:
以protected的方式繼承基類
結(jié)論:
在protected的繼承方式下:
①基類中public修飾的 成員在子類中訪問權(quán)限為protected
②基類中protected修飾的成員在子類中的訪問權(quán)限依舊是protected
③父類中的private訪問權(quán)限的變量在子類中不可見(不能直接被訪問)
以private的方式繼承基類
結(jié)論:
在private的繼承方式下:
①基類中public修飾的 成員在子類中訪問權(quán)限為private
②基類中protected修飾的成員在子類中的訪問權(quán)限為private
③父類中的private訪問權(quán)限的變量在子類中不可見(不能直接被訪問)
上面詳細(xì)分析了每一種個情況,下面我們針對上面的結(jié)論進(jìn)行匯總:
注意:
1、基類private成員在派生類中無論以什么方式繼承都是不可見的。這里的不可見是指基類的私有成員還是被繼承到了派生類對象中,但是語法上限制派生類對象不管在類里面還是類外面都不能去訪問它。
2、基類private成員在派生類中是不能被訪問,如果基類成員不想在類外直接被訪問,但需要在派生類中能訪問,就定義為protected??梢钥闯霰Wo(hù)成員限定符是因繼承才出現(xiàn)的
3、在實(shí)際運(yùn)用中一般使用都是public繼承,幾乎很少使用protetced/private繼承,也不提倡使用protetced/private繼承,因?yàn)閜rotetced/private繼承下來的成員都只能在派生類的類里面使用,實(shí)際中擴(kuò)展維護(hù)性不強(qiáng)。
2、class與struct的區(qū)別
主要有以下3點(diǎn)區(qū)別:
3、賦值兼容規(guī)則
前提:一定在public的繼承方式下才滿足
可以直接使用子類對象給父類對象賦值,反過來不行
這個很好理解,具體和可以通過兩個方面理解:
①子類和父類的關(guān)系是is–a的關(guān)系,因此使用子類給父類賦值時可以的
②從對象模型來說。
對象模型可以簡單理解為成員變量在內(nèi)存中的布局情況;
可以使用基類的指針指向子類的對象,反過來不行
如果一定要指向,必須強(qiáng)轉(zhuǎn),不推薦,僅僅是能通過編譯,但是在使用的時候可能會造成程序崩潰
分析如下:
可以使用基類的引用去引用子類對象,反過來不行
引用在底層本質(zhì)上就是使用指針實(shí)現(xiàn)的,因此它和指針的理解思路是一致的,這里就不再贅述。
4、繼承中的作用域問題
明確:派生類和基類隸屬于不同的作用域
那么,現(xiàn)在有這樣一種情況:
基類和派生類中出現(xiàn)了同名的成員變量或成員方法。這種情況要如何去理解呢? 首先,他一定不是函數(shù)重載,因?yàn)楹瘮?shù)重載的前提必須是在同一作用域。 其實(shí)它就是我們本模塊要介紹的----同名隱藏(重定義)問題
基類和派生類中出現(xiàn)同名的成員時,會有如下問題的存在:
那么,該如何解決呢?
只需要在訪問的時候加上 基類名稱和作用域限定符即可,這樣做的目的是明確告訴編譯器被調(diào)用成員所處的作用域
建議:一般情況下,在繼承體系中最好不要定義同名的成員
5、派生類(子類)的默認(rèn)成員函數(shù)
5.1 構(gòu)造函數(shù)
主要取決于基類的情況,分為兩大類進(jìn)行討論:
基類沒有顯式定義任何構(gòu)造函數(shù)
子類可以提供構(gòu)造函數(shù),也可以不提供構(gòu)造函數(shù) 是否提供根據(jù)子類中完成的功能或者具體情況決定
基類顯式定義了構(gòu)造函數(shù)
①基類的構(gòu)造函數(shù)是無參或者全缺省的
子類可以提供構(gòu)造函數(shù),也可以不提供構(gòu)造函數(shù)
是否提供根據(jù)子類中完成的功能或者具體情況決定
②基類的構(gòu)造函數(shù)是非默認(rèn)構(gòu)造函數(shù)
子類必須要定義自己的構(gòu)造函數(shù)
在子類構(gòu)造函數(shù)初始化列表位置顯式調(diào)用基類的構(gòu)造函數(shù)(完成從基類中繼承下來的成員的初始化工作)
基類和子類構(gòu)造函數(shù)的調(diào)用先后順序是怎樣的?
把握一點(diǎn):
創(chuàng)建那個類的對象,編譯器就會調(diào)用這個類的構(gòu)造函數(shù)
例如:創(chuàng)建子類對象,本質(zhì)上調(diào)用的是子類的構(gòu)造函數(shù),但是在子類的構(gòu)造函數(shù)的初始化列表處會調(diào)用基類的構(gòu)造方法來初始化從基類繼承下來的對象。然后再去執(zhí)行子類構(gòu)造函數(shù)的函數(shù)體。 因此,從結(jié)果上來看是基類對象的構(gòu)造函數(shù)先執(zhí)行完畢,子類構(gòu)造函數(shù)后執(zhí)行完畢。
5.2 拷貝構(gòu)造函數(shù)
取決于基類的情況,主要分為兩類:
基類的拷貝構(gòu)造函數(shù)未定義
子類的拷貝構(gòu)造函數(shù)可定義可不定義,根據(jù)子類的實(shí)際情況決定
基類的拷貝構(gòu)造函數(shù)定義了
子類也需要定義拷貝構(gòu)造函數(shù),并且需要在子類的拷貝構(gòu)造函數(shù)初始化列表的位置顯式調(diào)用基類的拷貝構(gòu)造函數(shù)
5.3 賦值運(yùn)算符重載
1.基類的賦值運(yùn)算符重載未定義
子類可定義可不定義
2.基類的賦值運(yùn)算符重載顯式定義了
子類也需要定義,分為兩個大的步驟:
①調(diào)用基類的賦值運(yùn)算符重載給基類部分成員賦值base::operator= (d);
②給子類自己新增的部分進(jìn)行賦值
注意:基類的operator= 與子類自己的 operator= 構(gòu)成了同名隱藏,因此要加作用域限定符指定調(diào)用基類的operator=,否則默認(rèn)調(diào)用子類自己的operator=,就會陷入無限遞歸
正確示范:
錯誤示范:
5.4 析構(gòu)函數(shù)
編譯器將子類的析構(gòu)函數(shù)編譯完成之后,會自動在子類析構(gòu)函數(shù)的最后一條語句之后插入一條調(diào)用基類析構(gòu)函數(shù)的匯編語句call ~Base();!
問題:基類和子類析構(gòu)函數(shù)調(diào)用先后順序?
6、基類中哪些成員被子類繼承了
6.1 成員變量
普通成員變量,全部被繼承!
這個我們在本文的1.2 定義這個模塊已經(jīng)全部驗(yàn)證!
靜態(tài)成員變量也被繼承了
注意:靜態(tài)成員變量在整個繼承體系中只有一份
驗(yàn)證:通過靜態(tài)變量來記錄創(chuàng)建對象的個數(shù)
class Base { public: Base(int a,int b) { _a = a; _b = b; ++_count; } Base(const Base& b) { _a = b._a; _b = b._b; ++_count; } Base& operator=(const Base& b) { _a = b._a; _b = b._b; return *this; } ~Base() { cout << "Base::~Base()" << endl; --_count; } public: int _a; int _b; static int _count; }; int Base::_count = 0; class Derived : public Base { public: Derived() :Base(1,2) { } Derived(int a,int b,int c) :Base(a,b) , _c(c) {} Derived(const Derived& d) :Base(d) { _c = d._c; } Derived& operator=(const Derived& d) { Base::operator=(d); _c = d._c; return *this; } ~Derived() { cout << "Derived::~Derived()" << endl; } public: int _c; }; void Test() { cout << &Base::_count << endl; cout << &Derived::_count << endl; }
6.2 成員方法
普通成員方法,被子類繼承了。
前面的代碼均有體現(xiàn),這里不再驗(yàn)證~
靜態(tài)成員方法—也被子類繼承了
驗(yàn)證:
7、友元函數(shù)被繼承了嗎
明確:友元函數(shù)不是類的成員函數(shù),他只是在一個類中進(jìn)行聲明,目的是打破類的封裝性去訪問原本外部不可訪問的成員。
這個問題很好測試,我們只需要為子類提供一個友元函數(shù)去訪問父類中的protected訪問權(quán)限的成員變量。讓一個子類繼承自父類,然后測試在父類中聲明的友元函數(shù)是否能夠訪問子類中的protected/private成員變量即可!
如果可以訪問,那就說明 友元函數(shù)也會被繼承下來。
如果不可以訪問,那就說明友元函數(shù)不會被繼承下來
直接上例子:
Test函數(shù)測試結(jié)果:
結(jié)論:友元函數(shù)不能被繼承!
本篇文章到這里就結(jié)束了,感覺有所幫助的讀友,可以轉(zhuǎn)發(fā)分享給身邊的朋友并留下你們的足跡!
下篇我們講講C++中一些不同的繼承體系~,我們下篇再見!
到此這篇關(guān)于C++深入探究繼承的概念與使用的文章就介紹到這了,更多相關(guān)C++繼承內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Qt GUI圖形圖像開發(fā)之QT表格控件QTableView,QTableWidget復(fù)雜表頭(多行表頭) 及凍結(jié)、固定特
這篇文章主要介紹了Qt GUI圖形圖像開發(fā)之QT表格控件QTableView,QTableWidget復(fù)雜表頭(多行表頭) 及凍結(jié)、固定特定的行的詳細(xì)方法與實(shí)例,需要的朋友可以參考下2020-03-03C語言實(shí)現(xiàn)查詢自動售貨機(jī)中的商品價格【實(shí)例分享】
本文主要介紹了C語言實(shí)現(xiàn)查詢自動售貨機(jī)中的商品價格的相關(guān)資料。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04boost.asio框架系列之調(diào)度器io_service
這篇文章介紹了boost.asio框架系列之調(diào)度器io_service,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06C++實(shí)現(xiàn)打印虛函數(shù)表的地址
對于存在虛函數(shù)的類,如何打印虛函數(shù)表的地址,并利用這個虛函數(shù)表的地址來執(zhí)行該類中的虛函數(shù)呢,下面小編就來和大家一起簡單聊聊吧2023-07-07