C++(22)--继承和派生
繼承和派生
- 1.基本概念
- 2.實(shí)現(xiàn)公有繼承
- 3.私有繼承的例子
- 4. 繼承和組合
《老九學(xué)堂C++課程》《C++ primer》學(xué)習(xí)筆記。《老九學(xué)堂C++課程》詳情請到B站搜索《老九零基礎(chǔ)學(xué)編程C++入門》
-------------簡單的事情重復(fù)做,重復(fù)的事情用心做,用心的事情堅(jiān)持做(老九君)---------------
1.基本概念
面向?qū)ο髈op–三大重要特性-- 封裝、繼承、多態(tài)
在C++中,代碼重用是通過“繼承(inheritance)”機(jī)制實(shí)現(xiàn)的。
在一個(gè)已經(jīng)存在的類的基礎(chǔ)上,再建立一個(gè)新類。
從已有的類中派生出新類,派生類就繼承了原有類(基類)的特征,包括成員和方法(以后函數(shù)就叫方法)
通過繼承可以完成的功能-可升級(jí)可維護(hù)
注意:
繼承機(jī)制只需要提供新的特性,甚至不需要訪問源碼就可以派生出類
允許在不公開的情況下將自己的類分發(fā)給他人,同時(shí)允許他們在類中添加新的特性。
程序升級(jí)和擴(kuò)展–非常忌諱的是,修改原有的代碼。原來的代碼測試通過了,測試實(shí)際是一件非常困難的事。
開發(fā)一款RPG(Role-playing Gam)游戲
游戲職業(yè):坦克,戰(zhàn)士,刺客,法師,射手,輔助
1.0 版本:戰(zhàn)士,法師
直接定義英雄類:戰(zhàn)士類,法師類–存在相同屬性和方法。
把相同的成員和方法封裝成基類。
注意:
1.派生類對(duì)象存儲(chǔ)了基類的數(shù)據(jù)成員
2.派生類對(duì)象可以調(diào)用基類的非私有函數(shù)
3.派生類需要自己的構(gòu)造方法
4.派生類根據(jù)需要增加額外的成員和方法
繼承的繼承:稱為直接基類和間接基類。
父類的的成員和方法的公有,私有,和受保護(hù)三種屬性的訪問權(quán)限:
1.公有權(quán)限下,自己和派生類,以及外部都能訪問
2.私有權(quán)限下,只有自己訪問,派生類和外部都無法訪問
3.受保護(hù)權(quán)限下,自己和派生類可以訪問,外部無法訪問
繼承分為公有繼承,私有繼承,受保護(hù)繼承。三種方式繼承之后子類權(quán)限的變化
| 公有 | 公有 | 私有 | 受保護(hù) |
| 受保護(hù) | 受保護(hù) | 私有 | 受保護(hù) |
| 私有 | 不被繼承 | 不被繼承 | 不被繼承 |
全部繼承,不封裝基類–公有繼承(除了基類的私有成員不繼承,其他都是權(quán)限不變的繼承)is a 關(guān)系。
全部繼承,完全封裝基類–私有繼承(庶出,除了基類的私有成員不繼承,其他成員繼承后權(quán)限改成私有) has a關(guān)系。使用包含來實(shí)現(xiàn)has a ,用繼承來實(shí)現(xiàn)有點(diǎn)抽象。
全部繼承,有選擇封裝基類–受保護(hù)(除了基類的私有成員不繼承,其他成員繼承后權(quán)限被改為受保護(hù)模式)
靈活運(yùn)用面向?qū)ο笏枷氲闹匾w現(xiàn)。
不管使用哪一種繼承,派生類都不能訪問基類里的私有成員,除非改成protected.
2.實(shí)現(xiàn)公有繼承
掌握公有繼承,了解私有繼承和受保護(hù)繼承。
滿足is a 關(guān)系的可以用繼承。
clion 還不會(huì)寫配置文件,類圖生成
類對(duì)象在內(nèi)存中的存儲(chǔ)情況
a)對(duì)象的成員變量存在堆內(nèi)存區(qū)/棧內(nèi)存區(qū),代碼存儲(chǔ)在公有的成員函數(shù)代碼區(qū)。所有的對(duì)象共同享有一段函數(shù)代碼
b)如果使用sizeof 求類所占空間的大小,只是計(jì)算了成員變量的大小,并沒有把成員函數(shù)也包含在內(nèi)。
a)派生類的內(nèi)存模型看成是基類成員變量和新增成員變量的總和,所有的成員函數(shù)仍然共有另一個(gè)區(qū)域–代碼區(qū)。
創(chuàng)建的時(shí)候先初始化基類再初始化派生類
釋放時(shí)先釋放派生類再釋放基類
demo1: Warrior 公有繼承
//main.cpp #include <iostream> #include <string> #include "Hero.h" #include "Warrior.h"using namespace std; void HeroTest(); void WarriorTest(); int main() {// HeroTest();WarriorTest(); } void HeroTest() {Hero hero1;cout << hero1 << endl;hero1.Move();Hero * hero2 = new Hero("測試英雄2",999,5000,5000);cout << *hero2 << endl;hero2->Move();//(*hero2).Move(); // 等價(jià)調(diào)用 } void WarriorTest(){Warrior warrior1;// 情況1:派生類中沒有重新實(shí)現(xiàn)move方法,調(diào)用父類方法// 情況2:派生類中重新實(shí)現(xiàn)move方法,調(diào)用子類實(shí)現(xiàn)的該方法warrior1.Move();cout << warrior1 << endl;Hero * hero = new Warrior; // 基類指針指向了派生類--標(biāo)準(zhǔn)的多態(tài)hero->Move(); // 調(diào)用基類的實(shí)現(xiàn)delete hero;} //Hero.h // // Created by 陳瑩瑩 on 2021/3/15. //#ifndef CHAPTER13_1_HERO_H #define CHAPTER13_1_HERO_H#include <string> #include <vector> #include <list> #include <iostream> #include <assert.h> using namespace std; class Hero { private:string m_NickName;int m_Level;int m_MaxLife;int m_CurrLife;int x;int y;public:Hero();Hero(const string& nickName);Hero(const string& nickName, int level);Hero(const string& nickName, int level, int maxLife, int currLife);void Move();friend ostream& operator<<(ostream& out, const Hero& hero);// friend ostream& operator<<(ostream& out, const* hero);string GetNickName() const{return m_NickName;}int GetLevel() const{return m_Level;}int GetMaxLife() const{return m_MaxLife;}int GetCurrLife() const{return m_CurrLife;}void SetNickName(const string & nickName){this->m_NickName = nickName;}void SetLevel(int level);void SetMaxLife(int maxLife);void SetCurrLife(int currLife);void operation1(); }; #endif //CHAPTER13_1_HERO_H //Hero.cpp // // Created by 陳瑩瑩 on 2021/3/15. //#include "Hero.h" #include <string> #include <vector> #include <list> #include <iostream> #include <assert.h> #include "Hero.h" using namespace std;Hero::Hero() : m_NickName("默認(rèn)英雄"),m_Level(1),m_MaxLife(100),m_CurrLife(100) {cout << "調(diào)用了Hero的默認(rèn)構(gòu)造" << endl; } //Hero::Hero(const string& nickName):m_NickName(nickName),m_Level(1),m_MaxLife(100),m_CurrLife(100) //{ // //} Hero::Hero(const string& nickName):Hero(nickName,1,100,10) {cout << "調(diào)用了Hero 一個(gè)參數(shù)版本的構(gòu)造" << endl; } Hero::Hero(const string& nickName, int level):Hero(nickName, level,100,10) {cout << "調(diào)用了Hero 兩個(gè)參數(shù)版本的構(gòu)造" << endl; } Hero::Hero(const string& nickName, int level, int maxLife, int currLife):m_NickName(nickName),m_Level(level),m_MaxLife(maxLife),m_CurrLife(currLife) {cout << "調(diào)用了Hero 四個(gè)參數(shù)版本的構(gòu)造" << endl; } void Hero::Move() {// 默認(rèn)移動(dòng)cout << "普通英雄" << m_NickName << "正在奔跑在艾澤拉斯大陸上" << endl; } ostream& operator<<(ostream& out, const Hero& hero){out << "昵稱:" << hero.GetNickName() << "\n";out << "等級(jí):" << hero.GetLevel() << "\n";out << "最大生命:" << hero.GetMaxLife() << "\n";out << "當(dāng)前生命:" << hero.GetCurrLife() ;return out; } void Hero::operation1() { } //Warrior.h // // Created by 陳瑩瑩 on 2021/3/16. // #ifndef CHAPTER13_1_WARRIOR_H #define CHAPTER13_1_WARRIOR_H #include "Hero.h" // 共有繼承-體現(xiàn)了is a 關(guān)系 class Warrior : public Hero{ private:int m_PhysicalAttack; public:Warrior();Warrior(const string& nickName, int phyAttack);void Move(); // 在派生類中實(shí)現(xiàn)派生類版本的move方法~Warrior(); }; #endif //CHAPTER13_1_WARRIOR_H //Warrior.cpp // // Created by 陳瑩瑩 on 2021/3/16. // #include "Warrior.h" Warrior::Warrior() :Hero("默認(rèn)構(gòu)造",1,100,100) { } Warrior::Warrior(const string& nickName, int phyAttack):Hero(nickName,1,100,100),m_PhysicalAttack(phyAttack) { } void Warrior::Move() {// m_NickName沒辦法過呀,不能訪父類的私有屬性,需要在將父類中私有成員改為受保護(hù)成員//cout << "戰(zhàn)士《" << m_NickName << "》"<< "背著一大堆近戰(zhàn)武器正在前進(jìn)。。。"<< endl;cout << "戰(zhàn)士《" << GetNickName() << "》"<< "背著一大堆近戰(zhàn)武器正在前進(jìn)。。。"<< endl; } Warrior::~Warrior() { }有關(guān)基類,派生類構(gòu)造
派生類與基類之間特殊關(guān)系小結(jié)
1.派生類可以使用基類的非私有成員函數(shù)(public和protect)
2.基類指針可以在不進(jìn)行顯示類型轉(zhuǎn)換的情況下指向派生類對(duì)象
Warrior warrior1("諸葛達(dá)摩", 10, 100, 100); Hero& refHero = warrior1; // 基類引用指向派生類, Hero* ptrHero = &warrior1; // 基類指針指向派生類對(duì)象 Warrior& warrior2 = (Warrior)refHero; // 父類引用/指針需要強(qiáng)制轉(zhuǎn)換成子類引用/指針(前提:父類指針指向子類對(duì)象) //不可以將基類對(duì)象的地址賦給派生類引用和對(duì)象,即不能進(jìn)行逆操作3.可以將派生類對(duì)象賦值給基類對(duì)象,程序會(huì)使用隱式重載賦值運(yùn)算符
Hero hero = warrior; hero.move(); //調(diào)用父類方法3.私有繼承的例子
沒講完呀,暫且收一收好了
//main.cpp #include <iostream> #include "Teacher.h" void TeacherTest(){Teacher teacher1(8000);// 名字設(shè)置沒有寫完 } int main() {TeacherTest();return 0; } //Teacher.h // // Created by 陳瑩瑩 on 2021/3/21. //#ifndef CHAPTER13_1_TEACHER_H #define CHAPTER13_1_TEACHER_H #include <iostream> #include <string> using namespace std; /** 用來演示私有繼承的其中一種用法* 實(shí)現(xiàn)組合關(guān)系,只能組合一個(gè)屬性不能組合兩個(gè)屬性* Teacher類中擁有string類型的成員name* */class Teacher :private string{ // teacher 中擁有string類型的成員 private:double salary; // 工資public:Teacher();Teacher(int _salary) : salary(_salary){}double GetSalary(){return salary;}void SetSalary(double salary){this->salary = salary;}// 難點(diǎn)const string& GetName() const{/** Teacher類是由string 類私有派生而來,所以,可以使用強(qiáng)制類型轉(zhuǎn)換,將Teacher類轉(zhuǎn)換成string類* 為了避免調(diào)用構(gòu)造函數(shù)創(chuàng)建新的對(duì)象,所以強(qiáng)制轉(zhuǎn)換成了string 的引用類型返回* 本方法返回一引用,指向調(diào)用本方法的Teacher 對(duì)象中繼承而來的string對(duì)象* */return (const string&)*this; //強(qiáng)轉(zhuǎn)}//using string::length(); //將字符串方法聲明為本類的公有方法/*返回當(dāng)前教師類對(duì)象姓名的字符串長度*/int GetLenght(){return string::length();}~Teacher();string nickName; // 使用組合關(guān)系實(shí)現(xiàn)比較簡單的has-a 關(guān)系protected:}; #endif //CHAPTER13_1_TEACHER_H4. 繼承和組合
一張臉由多個(gè)類組合而成,頭發(fā)的不同總類從父類頭發(fā)那里繼承而來。
繼承是is a 縱向關(guān)系–狗是哺乳動(dòng)物,戰(zhàn)士是英雄,橘貓是寵物–
組合是has a 橫向關(guān)系–學(xué)生有書包,戰(zhàn)士有武器,
繼承是C++與C最重要的區(qū)別,雖然C++也可以用C語言的編程習(xí)慣。
總結(jié)
以上是生活随笔為你收集整理的C++(22)--继承和派生的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网关服务器 .
- 下一篇: C++(23)--多态性与虚函数