柚子快報(bào)激活碼778899分享:筆記 【C++】繼承(上)
柚子快報(bào)激活碼778899分享:筆記 【C++】繼承(上)
個(gè)人主頁~
繼承
一、繼承的概念以及定義1、繼承的概念2、繼承的定義(1)定義格式(2)繼承基類成員訪問方式的變化
二、基類和派生類對(duì)象賦值轉(zhuǎn)換三、繼承中的作用域
一、繼承的概念以及定義
1、繼承的概念
繼承機(jī)制是面向?qū)ο蟪绦蛟O(shè)計(jì)使代碼可以復(fù)用的最重要的手段,它允許程序員在保持原有類特性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加功能,這樣產(chǎn)生新的類,被叫做派生類也叫子類,原來的類叫基類也叫父類,繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計(jì)的層次結(jié)構(gòu),體現(xiàn)了由簡(jiǎn)單到復(fù)雜的認(rèn)知過程,繼承是類設(shè)計(jì)層次的復(fù)用
以前我們接觸過的使代碼可以復(fù)用的一種手段就是封裝函數(shù),比如有三個(gè)函數(shù)都需要交換函數(shù),我們把交換函數(shù)封裝在外邊,然后在寫這三個(gè)函數(shù)的時(shí)候直接調(diào)用交換函數(shù)就可以了
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "little_monster"; // 姓名
int _age = 19; // 年齡
};
class Student : public Person
{
protected:
int _stu_id; // 學(xué)號(hào)
};
class Teacher : public Person
{
protected:
int _job_id; // 工號(hào)
};
void test1()
{
Student s;
Teacher t;
s.Print();
t.Print();
}
這是一個(gè)簡(jiǎn)單的用了繼承的代碼,Person對(duì)于Student和Teacher來說是一個(gè)父類(基類),Student和Teacher就是子類(派生類),繼承之后父類的成員都會(huì)成為子類的一部分
我們來看調(diào)試過程,這就是調(diào)用Print函數(shù)的前后兩步,通過調(diào)試窗口我們很清楚地看到這里Student類的Print直接復(fù)用Person類的Print
2、繼承的定義
(1)定義格式
class Student : public Person
{
protected:
int _stu_id; // 學(xué)號(hào)
};
Student:派生類(子類)
public:繼承方式,包括public、protected、private
Person:基類(父類)
我們發(fā)現(xiàn)繼承方式有三種,和訪問限定符的三種是一樣的
(2)繼承基類成員訪問方式的變化
類成員/繼承方式public繼承protected繼承private繼承基類的public成員派生類的public成員派生類的protected成員派生類的private成員基類的protected成員派生類的protected成員派生類的protected成員派生類的private成員基類的private成員派生類中不可見派生類中不可見派生類中不可見
總結(jié):
基類private成員在派生類中無論以什么方式繼承都是不可見的,這里的不可見是指基類的私有成員還是被繼承到了派生類對(duì)象中,但是語法上限制派生類對(duì)象不管在類里面還是類外面都不能去訪問它
基類private成員在派生類中是不能被訪問,如果基類成員不想在類外直接被訪問,但需要在派生類中能訪問,就定義為protected,可以看出保護(hù)成員限定符是因繼承才出現(xiàn)的,我們之前學(xué)習(xí)的過程中說protected和private是等價(jià)的,但在學(xué)習(xí)繼承后,我們就知道它們的區(qū)別了
在看完表格后我們發(fā)現(xiàn)一個(gè)規(guī)律,對(duì)于可見性來說public>protected>private,除了基類的private成員在派生類中不可見以外,其他所有的繼承,都是取繼承方式與訪問限定符中可見性較小的那一個(gè)作為派生類的訪問限定符
使用關(guān)鍵字class時(shí)默認(rèn)的繼承方式是private,使用struct時(shí)默認(rèn)的繼承方式是public,不過最好顯示的寫出繼承方式
在實(shí)際運(yùn)用中一般使用都是public繼承,幾乎很少使用protetced/private繼承,也不提倡使用protetced/private繼承,因?yàn)閜rotetced/private繼承下來的成員都只能在派生類的類里面使用,實(shí)際中擴(kuò)展維護(hù)性不強(qiáng),也就是說上面的表格內(nèi)容不要死記硬背,現(xiàn)實(shí)場(chǎng)景下真正用到的沒有那么多,按照上面的規(guī)律來記憶就可以
class Person
{
public:
void Print()
{
cout << _name << endl;
}
protected:
string _name; // 姓名
private:
int _age; // 年齡
};
//class Student : protected Person
//class Student : private Person
class Student : public Person
{
protected:
int _stu_id; // 學(xué)號(hào)
};
像是如上代碼,在繼承方式為public時(shí),Person當(dāng)中的Print為public可以在派生類內(nèi)外調(diào)用,_name為protected可以在派生類內(nèi)調(diào)用,_age在派生類內(nèi)不可見
在繼承方式為protected時(shí),Person當(dāng)中的Print為protected可以在派生類內(nèi)調(diào)用,_name為protected可以在派生類內(nèi)調(diào)用,_age在派生類內(nèi)不可見
在繼承方式為private時(shí),Person當(dāng)中的三個(gè)成員在派生類內(nèi)都不可見
二、基類和派生類對(duì)象賦值轉(zhuǎn)換
派生類對(duì)象可以賦值給基類的對(duì)象、基類的指針、基類的引用,我們把它叫做切割,因?yàn)橐话銇碚f派生類的成員包括但不限于基類的成員,將除了基類以外的成員切割掉然后賦值給基類就叫切割
基類對(duì)象不能賦值給派生類
基類的指針或者引用可以通過強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針或者引用,但是必須是基類的指針是指向派生類對(duì)象時(shí)才是安全的,這部分我們?cè)诙鄳B(tài)的內(nèi)容中會(huì)詳細(xì)介紹
這里我們復(fù)習(xí)一個(gè)內(nèi)容,就是前面講的臨時(shí)變量具有常性
double d = 1.1;
int i = d;
這里我們看到,當(dāng)二者的類型不相同時(shí),第二句代碼不是d直接給到i,而是d產(chǎn)生一個(gè)臨時(shí)變量,然后由這個(gè)臨時(shí)變量給到i 我們看到i是不能直接做d的別名的,而加一個(gè)const修飾就可以取到別名,這就是臨時(shí)變量具有常性的證明 不管是什么類型的常性變量,我們都要加const修飾以后才能引用它 但是看如下的情況 這是沒有任何問題的,因?yàn)樽宇悓?duì)象賦值給父類對(duì)象、父類指針、父類的引用被認(rèn)為是天然的,中間不產(chǎn)生臨時(shí)對(duì)象,這叫做父子類賦值兼容規(guī)則(切割)
class Person
{
protected:
string _name; // 姓名
string _sex; // 性別
int _age; // 年齡
};
class Student : public Person
{
public:
int _stu_id; // 學(xué)號(hào)
};
void Test()
{
Student stu;
// 1.子類對(duì)象可以賦值給父類對(duì)象/指針/引用
Person per = stu;
Person* pp = &stu;
Person& rp = stu;
//2.基類對(duì)象不能賦值給派生類對(duì)象
//sobj = pobj;
// 3.基類的指針可以通過強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針
pp = &stu;
Student * ps1 = (Student*)pp; // 這種情況轉(zhuǎn)換時(shí)可以的
ps1->_stu_id = 10;
//pp = &per;
//Student* ps2 = (Student*)pp; // 這種情況轉(zhuǎn)換時(shí)雖然可以,但是會(huì)存在越界訪問的問題
//ps2->_stu_id = 10;
}
這里上面的代碼都比較容易理解,下邊這一塊,也就是基類的指針可以通過強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針這里,分了兩種狀況
上面這種可以是因?yàn)閟tu本身就是Student類型,所以ps1指向的實(shí)際上是stu的起始地址,這是合法的,因此可以安全地通過ps1訪問_stu_id成員
下面這種不可以是因?yàn)閜er并非Student類型,所以pp并不指向一個(gè)包含_stu_id成員的Student對(duì)象,當(dāng)通過ps2訪問_stu_id時(shí),實(shí)際上是在訪問per對(duì)象的內(nèi)存,但是按照Student的布局來解釋這塊內(nèi)存,這可能導(dǎo)致越界訪問、未定義行為,或者程序崩潰
三、繼承中的作用域
在繼承體系中基類和派生類都有獨(dú)立的作用域
子類和父類中有同名成員,子類成員將屏蔽父類對(duì)同名成員的直接訪問,這種情況叫隱藏,也叫重定義,在子類成員函數(shù)中,可以使用 基類::基類成員 顯示訪問
需要注意的是如果是成員函數(shù)的隱藏,只需要函數(shù)名相同就構(gòu)成隱藏
注意在實(shí)際中在繼承體系里面最好不要定義同名的成員
class Person
{
protected:
string _name = "little_monster"; // 姓名
int _id = 111; // 身份證號(hào)
};
class Student : public Person
{
public:
void Print()
{
cout << " name:" << _name << endl;
cout << " id:" << Person::_id << endl;
cout << " stu_id:" << _id << endl;
}
protected:
int _id = 999; // 學(xué)號(hào)
};
void Test()
{
Student s1;
s1.Print();
};
可以看到我們直接調(diào)用_id的話打印的是Student類中的_id,這就是成員函數(shù)的隱藏,當(dāng)我們想要調(diào)用Person類的_id時(shí),就常規(guī)調(diào)用Person::_id 就可以了
當(dāng)然這不只是變量,函數(shù)在重名時(shí)也會(huì)發(fā)生成員函數(shù)的隱藏,我們遵循就近原則,而且基類與派生類成員函數(shù)重名時(shí)不構(gòu)成函數(shù)重載而是隱藏
今日分享就到這了~
柚子快報(bào)激活碼778899分享:筆記 【C++】繼承(上)
好文閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。