網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
我們都知道面向?qū)ο笳Z(yǔ)言的三大特點(diǎn)是:**封裝,繼承,多態(tài)。**之前在類(lèi)和對(duì)象部分,我們提到了C++中的封裝,那么今天呢,我們來(lái)學(xué)習(xí)一下C++中的繼承。
繼承概念及定義
繼承概念
繼承(inheritance)機(jī)制是面向?qū)ο蟪绦蛟O(shè)計(jì)使代碼可以復(fù)用的最重要的手段,它允許程序員在保持原有類(lèi)特性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加功能,這樣產(chǎn)生新的類(lèi),稱(chēng)派生類(lèi)。繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計(jì)的層次結(jié)構(gòu),體現(xiàn)了由簡(jiǎn)單到復(fù)雜的認(rèn)知過(guò)程。以前我們接觸的復(fù)用都是函數(shù)復(fù)用,繼承是類(lèi)設(shè)計(jì)層次的復(fù)用
看概念是一件很讓人疑惑的東西,接下來(lái)我就來(lái)舉個(gè)例子來(lái)看看繼承具體是什么東西
首先我們定義兩個(gè)類(lèi),一個(gè)Student類(lèi),一個(gè)Teacher類(lèi),二者都有年齡和姓名,學(xué)生有學(xué)號(hào),老師有工號(hào)。
class Student
{
private:
int _age; //年齡
string _name; //姓名
int _stuid; //學(xué)號(hào)
};
class Teacher
{
private:
int _age; //年齡
string _name; //姓名
int _jobid; //工號(hào)
};
我們發(fā)現(xiàn)這兩個(gè)類(lèi)有一些重復(fù)的地方,比如年齡和姓名,這二者是他們的成員,此時(shí)代碼就產(chǎn)生了冗余。那么我們可不可以像個(gè)方法去復(fù)用這兩個(gè)成員呢?繼承此時(shí)就可以發(fā)揮它的重大作用。
我們將他們重復(fù)的地方提取出來(lái),重新定義一個(gè)Person類(lèi)。而Student類(lèi)和Teacher類(lèi)將Person類(lèi)繼承下來(lái),此時(shí)我們就實(shí)現(xiàn)了代碼的復(fù)用。
class Person
{
protected:
int _age; //年齡
string _name; //姓名
};
class Student : public Person
{
private:
int _stuid; //學(xué)號(hào)
};
class Teacher : public Person
{
private:
int _jobid; //工號(hào)
};
我們首先分別用Student和Teacher類(lèi)來(lái)創(chuàng)建兩個(gè)對(duì)象,來(lái)看看對(duì)象里面有什么。
int main()
{
Teacher t;
Student s;
return 0;
}
此時(shí)我們可以看到我們創(chuàng)建的兩個(gè)對(duì)象里面都含有從Person類(lèi)中繼承過(guò)來(lái)的 age 和 name 兩個(gè)成員。
所以繼承實(shí)際上是一個(gè)代碼的復(fù)用,我們可以借用已完成的類(lèi)的代碼來(lái)完善我們需要?jiǎng)?chuàng)造的新類(lèi)。
繼承定義
以我們剛剛創(chuàng)建的Student類(lèi)來(lái)舉例:我們看到Person是父類(lèi),也稱(chēng)作基類(lèi)。Student是子類(lèi),也稱(chēng)作派生類(lèi)。
繼承方式
我們?cè)陬?lèi)和對(duì)象的時(shí)候介紹了三種訪問(wèn)限定符:public(公有),protected(保護(hù))和private(私有)。訪問(wèn)限定符限定了我們?cè)陬?lèi)外如何去訪問(wèn)類(lèi)中的成員。
在繼承中我們一樣使用這三種限定符來(lái)限定子類(lèi)該如何去訪問(wèn)父類(lèi)的的成員,下面有一張表來(lái)表示他們的關(guān)系。
類(lèi)成員\繼承方式 | public繼承 | protected繼承 | private繼承 |
---|---|---|---|
父類(lèi)的public成員 | 派生類(lèi)的public成員 | 派生類(lèi)的protected成員 | 派生類(lèi)的private成員 |
父類(lèi)的protected成員 | 派生類(lèi)的protected成員 | 派生類(lèi)的protected成員 | 派生類(lèi)的private成員 |
父類(lèi)的private成員 | 在派生類(lèi)中不可見(jiàn) | 在派生類(lèi)中不可見(jiàn) | 在派生類(lèi)中不可見(jiàn) |
首先解釋一下在派生類(lèi)中不可見(jiàn)是什么意思,就如同我們?cè)陬?lèi)外無(wú)法直接去修改類(lèi)中的private成員一樣,我們?cè)谧宇?lèi)中也無(wú)法直接修改父類(lèi)的private成員。
如何簡(jiǎn)潔的去記這個(gè)表呢?在C++中權(quán)限的關(guān)系:public > protected > private。在繼承的時(shí)候呢,父類(lèi)成員的權(quán)限取的是:父類(lèi)成員原本權(quán)限和繼承方式中較小的那個(gè)。
比如父類(lèi)的A成員原本權(quán)限為public,而子類(lèi)的繼承方式為private。此時(shí)A成員相對(duì)子類(lèi)來(lái)說(shuō)就為private成員
父類(lèi)和子類(lèi)對(duì)象賦值轉(zhuǎn)換
子類(lèi)的對(duì)象可以賦值給 父類(lèi)的對(duì)象/父類(lèi)的指針/父類(lèi)的應(yīng)用,那么是如何進(jìn)行賦值的呢?形象一點(diǎn)來(lái)說(shuō)就是切片,將子類(lèi)中父類(lèi)的部分切割父類(lèi)。
但我們無(wú)法反過(guò)來(lái),將父類(lèi)對(duì)象賦值給子類(lèi)對(duì)象。
繼承中的作用域
在繼承體系中父類(lèi)和子類(lèi)都有獨(dú)立的作用域,如果子類(lèi)和父類(lèi)中有同名的成員,子類(lèi)成員將屏蔽對(duì)父類(lèi)成員的直接訪問(wèn),這種情況叫隱藏,也叫重定義。
下面還是用我們的Person類(lèi)和Student類(lèi)來(lái)舉個(gè)栗子,我們分別在Person類(lèi)和Student類(lèi)中加入一個(gè)print函數(shù),通過(guò)打印內(nèi)容來(lái)區(qū)分調(diào)用的為哪一print函數(shù)。
class Person
{
protected:
int _age;
string _name;
public:
void print()
{
cout << "Person"<< endl;
}
};
class Student : public Person
{
private:
int _stuid; //學(xué)號(hào)
public:
void print()
{
cout << "Student" << endl;
}
};
接下來(lái)我們創(chuàng)建一個(gè)對(duì)象然后來(lái)試一下結(jié)果。
int main()
{
Student s;
s.print();
return 0;
}
我們可以看到我們調(diào)用的為Student中的print函數(shù)。此時(shí)子類(lèi)的print函數(shù)已經(jīng)對(duì)父類(lèi)的print函數(shù)進(jìn)行了重定義。重定義不代表子類(lèi)無(wú)法去調(diào)用父類(lèi)的同名函數(shù),只是不那么直接而已。使用下面這種方法我們就可以調(diào)用父類(lèi)中的同名函數(shù)。
int main()
{
Student s;
s.Person::print();
return 0;
}
通過(guò)指定類(lèi)域,我們就可以去調(diào)用父類(lèi)的print函數(shù)。但在實(shí)際中最好不要去定義同名函數(shù)以免帶來(lái)問(wèn)題。
派生類(lèi)的默認(rèn)成員函數(shù)
首先我們來(lái)回顧一下有哪幾個(gè)默認(rèn)成員函數(shù)。
那么在子類(lèi)中,這些默認(rèn)成員函數(shù)是怎么生成的呢?
1.子類(lèi)的構(gòu)造函數(shù)必須調(diào)用父類(lèi)的構(gòu)造函數(shù)初始化父類(lèi)的那一部分成員。如果父類(lèi)沒(méi)有默認(rèn)的構(gòu)造函數(shù),則必須在派生類(lèi)構(gòu)造函數(shù)的初始化列表中顯式調(diào)用。還是用我們的Person類(lèi)和Student類(lèi)舉例。
情況一:有默認(rèn)構(gòu)造函數(shù)
class Person
{
protected:
int _age;
string _name;
public:
Person()
{
cout << "Person" << endl; //調(diào)用就打印
}
};
class Student : public Person
{
private:
int _stuid; //學(xué)號(hào)
public:
Student()
{
cout << "Student" << endl; //調(diào)用就打印
}
};
int main()
{
Student s;
return 0;
}
情況二:無(wú)默認(rèn)構(gòu)造函數(shù)
class Person
{
protected:
int _age;
string _name;
public:
Person(int age, string name)
{
cout << "Person" << endl;
}
};
class Student : public Person
{
private:
int _stuid; //學(xué)號(hào)
public:
Student()
: Person(19, "wanku") //無(wú)默認(rèn)構(gòu)造,此時(shí)我們需要在初始化列表中初始化
{
cout << "Student" << endl;
}
};
int main()
{
Student s;
return 0;
}
int main()
{
Student s;
return 0;
}
2.子類(lèi)的拷貝構(gòu)造函數(shù)必須調(diào)用父類(lèi)的拷貝構(gòu)造完成父類(lèi)的拷貝初始化化。
class Person
{
protected:
int _age;
string _name;
public:
Person(int age = 10, string name = "wanku")
{
cout << "Person" << endl;
}
Person(const Person &p)
: _age(p._age), _name(p._name)
{}
};
class Student : public Person
{
private:
int _stuid; //學(xué)號(hào)
public:
Student()
{
cout << "Student" << endl;
}
Student(const Student &s)
: Person(s) /*顯示調(diào)用父類(lèi)的拷貝構(gòu)造*/, _stuid(s._stuid)
{}
};
有些朋友可能會(huì)疑惑,在Person類(lèi)中的拷貝構(gòu)造函數(shù)參數(shù)明明是Person類(lèi),為什么我們的Student類(lèi)可以傳過(guò)去呢?那是因?yàn)槲覀儎倓傊v的切片原理,當(dāng)我們把子類(lèi)對(duì)象傳過(guò)去時(shí),編譯器會(huì)進(jìn)行切分,然后再傳給父類(lèi)。
3.派生類(lèi)的operator=必須要調(diào)用基類(lèi)的operator=完成基類(lèi)的復(fù)制。(原理和拷貝構(gòu)造大體相似,值得注意的是:當(dāng)我們?cè)谧宇?lèi)直接想去調(diào)用父類(lèi)的operator= 時(shí),會(huì)發(fā)生重定義,使用時(shí)記得加上父類(lèi)的作用域)
4.在繼承中一個(gè)對(duì)象的歷程如下:父類(lèi)的構(gòu)造函數(shù) –> 子類(lèi)的構(gòu)造函數(shù) –> 子類(lèi)的析構(gòu)函數(shù) –> 父類(lèi)的析構(gòu)函數(shù)。這個(gè)過(guò)程相當(dāng)于把這些行為存在一個(gè)棧中,然后再把行為從棧中拿出來(lái)一般
派生類(lèi)的友元與靜態(tài)成員
父類(lèi)的友元不是子類(lèi)的友元。(你爸爸的朋友不一定是你的朋友)
父類(lèi)中有一個(gè)靜態(tài)成員,那么子類(lèi)和父類(lèi)共用一個(gè)靜態(tài)成員。(靜態(tài)成員并不存在對(duì)象中,只開(kāi)辟一個(gè)空間,所以只能共用一個(gè))
繼承關(guān)系
單繼承
一個(gè)子類(lèi)只有一個(gè)直接父類(lèi)。
多繼承
一個(gè)子類(lèi)有兩個(gè)及以上的父類(lèi)
菱形繼承
多繼承的一種特殊情況
原文鏈接:https://blog.csdn.net/m0_60447315/article/details/125909713
相關(guān)推薦
- 2023-02-23 Python利用Flask動(dòng)態(tài)生成漢字頭像_python
- 2022-12-28 C++關(guān)鍵字const使用方法詳解_C 語(yǔ)言
- 2022-05-08 一篇文章詳細(xì)解釋C++的友元(friend)_C 語(yǔ)言
- 2022-09-26 使用JDBC連接數(shù)據(jù)庫(kù)執(zhí)行sql語(yǔ)句,創(chuàng)建數(shù)據(jù)庫(kù)連接池
- 2023-07-22 IntelliJ Compilation Error zip END header not foun
- 2022-09-27 python?實(shí)現(xiàn)打印掃描效果詳情_(kāi)python
- 2022-03-30 Docker搭建RabbitMQ集群的方法步驟_docker
- 2022-08-26 C++宏函數(shù)和內(nèi)聯(lián)函數(shù)的使用_C 語(yǔ)言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支