C++繼承的定義與注意事項(xiàng)
一、什么是繼承?
1.1、概念
**繼承(inheritance)機(jī)制是面向?qū)ο蟪绦蛟O(shè)計(jì)使代碼可以復(fù)用的最重要的手段,它允許程序員在保持原有類特性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加功能,這樣產(chǎn)生新的類,稱派生類。
1.2、定義
下面我們看到Person是父類,也稱作基類。Student是子類,也稱作派生類
1.2.1、繼承關(guān)系和訪問(wèn)限定符
1.2.2、繼承基類成員訪問(wèn)方式的變化
// 實(shí)例演示三種繼承關(guān)系下基類成員的各類型成員訪問(wèn)關(guān)系的變化 class Person { public : void Print () { cout<<_name <<endl; } protected : string _name ; // 姓名 private : int _age ; // 年齡 }; //三種繼承方式,對(duì)應(yīng)上面圖表 //class Student : protected Person //class Student : private Person class Student : public Person { protected : int _stunum ; // 學(xué)號(hào) };
注意:
1.基類private成員在派生類中無(wú)論以什么方式繼承都是不可見(jiàn)的。不可見(jiàn)是指基類的私有成員還是被繼承到了派生類對(duì)象中,但是語(yǔ)法上派生類對(duì)像無(wú)法訪問(wèn);
2.使用關(guān)鍵字class時(shí)默認(rèn)的繼承方式是private,使用struct時(shí)默認(rèn)的繼承方式是public,不過(guò)最好顯示的寫(xiě)出繼承方式;
3. 在實(shí)際運(yùn)用中一般使用都是public繼承,不提倡使protetced/private繼承,因?yàn)閜rotetced/private繼承下來(lái)的成員都只能在派生類的類里面使用,實(shí)際中擴(kuò)展維護(hù)性不強(qiáng);
二、基類和派生類對(duì)象賦值轉(zhuǎn)換
派生類(子類)對(duì)象可以賦值給基類(父類)對(duì)象/指針/引用;
即將父類中那部分賦值給基類;
這里有個(gè)形象的說(shuō)法叫切片或者切割。寓意把派生類中父類那部分切來(lái)賦值過(guò)去。
基類對(duì)象不能賦值給派生類對(duì)象;
基類的指針可以通過(guò)強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針。但是必須是基類的指針是指向派生類對(duì)象時(shí)才是安全的。這里基類如果是多態(tài)類型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 來(lái)進(jìn)行識(shí)別后進(jìn)行安全轉(zhuǎn)換;
Student sobj ; // 1.子類對(duì)象可以賦值給父類對(duì)象/指針/引用 Person pobj = sobj ; Person* pp = &sobj; Person& rp = sobj;
//2.基類對(duì)象不能賦值給派生類對(duì)象 sobj = pobj;//錯(cuò)誤
// 3.基類的指針可以通過(guò)強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針 pp = &sobj Student* ps1 = (Student*)pp; // 這種情況轉(zhuǎn)換時(shí)可以的。 ps1->_No = 10; pp = &pobj; Student* ps2 = (Student*)pp; // 這種情況轉(zhuǎn)換時(shí)雖然可以,但是會(huì)存在越界訪問(wèn)的問(wèn)題 ps2->_No = 10;
三、繼承中的作用域
- 在繼承體系中基類和派生類都有獨(dú)立的作用域。
- 子類和父類中有同名成員,子類成員將屏蔽父類對(duì)同名成員的直接訪問(wèn),這種情況叫隱藏,也叫重定
- 義。(在子類成員函數(shù)中,可以使用 基類::基類成員 顯示訪問(wèn))
- 需要注意的是如果是成員函數(shù)的隱藏,只需要函數(shù)名相同就構(gòu)成隱藏。
- 注意在實(shí)際中在繼承體系里面最好不要定義同名的成員。
// Student的_num和Person的_num構(gòu)成隱藏關(guān)系,可以看出這樣代碼雖然能跑,但是非常容易混淆 class Person { protected : string _name = "小李子"; // 姓名 int _num = 111; // 身份證號(hào) }; class Student : public Person { public: void Print() { cout<<" 姓名:"<<_name<< endl; cout<<" 身份證號(hào):"<<Person::_num<< endl; cout<<" 學(xué)號(hào):"<<_num<<endl; } protected: int _num = 999; // 學(xué)號(hào) }; void Test() { Student s1; s1.Print(); };
// B中的fun和A中的fun不是構(gòu)成重載,因?yàn)椴皇窃谕蛔饔糜? // B中的fun和A中的fun構(gòu)成隱藏,成員函數(shù)滿足函數(shù)名相同就構(gòu)成隱藏。 class A { public: void fun() { cout << "func()" << endl; } }; class B : public A { public: void fun(int i) { A::fun(); cout << "func(int i)->" <<i<<endl; } }; void Test() { B b; b.fun(10); };
四、派生類的默認(rèn)成員函數(shù)
6個(gè)默認(rèn)成員函數(shù),“默認(rèn)”的意思就是指我們不寫(xiě),編譯器會(huì)變我們自動(dòng)生成一個(gè),那么在派生類中,這幾個(gè)
成員函數(shù)是如何生成的呢?
- 派生類的構(gòu)造函數(shù)必須調(diào)用基類的構(gòu)造函數(shù)初始化基類的那一部分成員。如果基類沒(méi)有默認(rèn)的構(gòu)造函
- 數(shù),則必須在派生類構(gòu)造函數(shù)的初始化列表階段顯示調(diào)用。
- 派生類的拷貝構(gòu)造函數(shù)必須調(diào)用基類的拷貝構(gòu)造完成基類的拷貝初始化。
- 派生類的operator=必須要調(diào)用基類的operator=完成基類的復(fù)制。
- 派生類的析構(gòu)函數(shù)會(huì)在被調(diào)用完成后自動(dòng)調(diào)用基類的析構(gòu)函數(shù)清理基類成員。因?yàn)檫@樣才能保證派生類
- 對(duì)象先清理派生類成員再清理基類成員的順序。
- 派生類對(duì)象初始化先調(diào)用基類構(gòu)造再調(diào)派生類構(gòu)造。
- 派生類對(duì)象析構(gòu)清理先調(diào)用派生類析構(gòu)再調(diào)基類的析構(gòu)。
五、繼承與友元
友元關(guān)系不能繼承,也就是說(shuō)基類友元不能訪問(wèn)子類私有和保護(hù)成員;
六、繼承與靜態(tài)成員
基類定義了static靜態(tài)成員,則整個(gè)繼承體系里面只有一個(gè)這樣的成員;
七、菱形繼承
單繼承:一個(gè)子類只有一個(gè)直接父類
多繼承:一個(gè)子類有兩個(gè)或以上直接父類時(shí)稱這個(gè)繼承關(guān)系為多繼承
菱形繼承:菱形繼承是多繼承的一種特殊情況。
菱形繼承的問(wèn)題:從下面的對(duì)象成員模型構(gòu)造,可以看出菱形繼承有數(shù)據(jù)冗余和二義性的問(wèn)題。在Assistant的對(duì)象中Person成員會(huì)有兩份。
虛擬繼承可以解決菱形繼承的二義性和數(shù)據(jù)冗余的問(wèn)題,在Student和Teacher的繼承;
加關(guān)鍵字:virtual
菱形繼承的內(nèi)存對(duì)象成員模型:這里可以看到數(shù)據(jù)冗余
菱形虛擬繼承的內(nèi)存對(duì)象成員模型:這里可以分析出D對(duì)象中將A放到的了對(duì)象組成的最下面,這個(gè)A同時(shí)屬于B和C,那么B和C如何去找到公共的A呢?
這里是通過(guò)了B和C的兩個(gè)指針,指向的一張表。這兩個(gè)指針叫虛基表指針,這兩個(gè)表叫虛基表。虛基表中存的偏移量。通過(guò)偏移量可以找到下面的A。
虛基表:存放偏移量
八、繼承和組合
public繼承是一種is-a的關(guān)系。也就是說(shuō)每個(gè)派生類對(duì)象都是一個(gè)基類對(duì)象,高耦合;
組合是一種has-a的關(guān)系。假設(shè)B組合了A,每個(gè)B對(duì)象中都有一個(gè)A對(duì)象,低耦合;
優(yōu)先使用對(duì)象組合,而不是類繼承
繼承和耦合的使用:
// Car和BMW Car和Benz構(gòu)成is-a的關(guān)系 class Car{ protected: string _colour = "白色"; // 顏色 string _num = "陜ABIT00"; // 車牌號(hào) }; class BMW : public Car{ public: void Drive() {cout << "好開(kāi)-操控" << endl;} }; class Benz : public Car{ public: void Drive() {cout << "好坐-舒適" << endl;} };
// Tire和Car構(gòu)成has-a的關(guān)系 class Tire{ protected: string _brand = "Michelin"; // 品牌 size_t _size = 17; // 尺寸 }; class Car{ protected: string _colour = "白色"; // 顏色 string _num = "陜ABIT00"; // 車牌號(hào) Tire _t; // 輪胎 };
總結(jié)
到此這篇關(guān)于C++繼承的定義與注意事項(xiàng)的文章就介紹到這了,更多相關(guān)C++繼承內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Unix下C程序內(nèi)存泄漏檢測(cè)工具Valgrind的安裝與使用詳解
以下是對(duì)Unix下C程序內(nèi)存泄漏檢測(cè)工具Valgrind的安裝與使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下2013-08-08Qt使用QCamera實(shí)現(xiàn)切換相機(jī),分辨率和圖像捕獲功能
這篇文章主要為大家介紹了如何利用Qt中的相機(jī)類QCamera,取景器類QCameraViewfinder,圖像捕獲類QCameraImageCapture實(shí)現(xiàn)切換相機(jī)、分辨率和圖像捕獲功能,需要的可以了解一下2023-04-04基于對(duì)話框程序中讓對(duì)話框捕獲WM_KEYDOWN消息的實(shí)現(xiàn)方法
下面我們將通過(guò)程序給大家演示基于對(duì)話框的應(yīng)用程序?qū)M_KEYDOWN消息的捕獲。需要的朋友可以參考下2013-05-05C++連接數(shù)據(jù)庫(kù)SqlServer、MySql、Oracle、Access、SQLite、PostgreSQL、Mong
C++是一種通用的編程語(yǔ)言,可以使用不同的庫(kù)和驅(qū)動(dòng)程序來(lái)連接各種數(shù)據(jù)庫(kù),以下是一些示例代碼,演示如何使用?C++?連接?SQL?Server、MySQL、Oracle、ACCESS、SQLite?、?PostgreSQL、MongoDB、Redis數(shù)據(jù)庫(kù)2024-08-08C++ using namespace std 用法深入解析
以下是對(duì)C++中using namespace std的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下2013-07-07MFC設(shè)置對(duì)話框焦點(diǎn)的方法簡(jiǎn)述
這篇文章主要介紹了MFC設(shè)置對(duì)話框焦點(diǎn)的方法簡(jiǎn)述,主要講述了兩種實(shí)現(xiàn)方法,需要的朋友可以參考下2014-10-10c++ 判斷是64位還是32位系統(tǒng)的實(shí)例
這篇文章主要介紹了c++ 判斷是64位還是32位系統(tǒng)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12