摘要:繼承繼承,就是子類(lèi)繼承父親的特征和行為,使得子類(lèi)具有父類(lèi)的成員變量和方法。此時(shí),被繼承的類(lèi)稱(chēng)為父類(lèi)或基類(lèi),而繼承的類(lèi)稱(chēng)為子類(lèi)或派生類(lèi)。,如果存在繼承關(guān)系的時(shí)候,和就不一樣了基類(lèi)中的成員可以在派生類(lèi)中使用,但是基類(lèi)中的成員不能再派生類(lèi)中使用。
c++在線編譯工具,可快速進(jìn)行實(shí)驗(yàn): https://www.dooccn.com/cpp/
這段時(shí)間打算重新把c++撿起來(lái), 實(shí)習(xí)給我的一個(gè)體會(huì)就是算法工程師是去解決實(shí)際問(wèn)題的,所以呢,不能被算法或者工程局限住,應(yīng)時(shí)刻提高解決問(wèn)題的能力,在這個(gè)過(guò)程中,我發(fā)現(xiàn)cpp很重要, 正好這段時(shí)間也在接觸些c++開(kāi)發(fā)相關(guān)的任務(wù),所有想借這個(gè)機(jī)會(huì)把c++重新學(xué)習(xí)一遍。 在推薦領(lǐng)域, 目前我接觸到的算法模型方面主要是基于Python, 而線上的服務(wù)全是c++(算法側(cè), 業(yè)務(wù)那邊基本上用go),我們所謂的模型,也一般是訓(xùn)練好部署上線然后提供接口而已。所以現(xiàn)在也終于知道,為啥只單純熟悉Python不太行了, cpp,才是yyds。
和python一樣, 這個(gè)系列是重溫,依然不會(huì)整理太基礎(chǔ)性的東西,更像是查缺補(bǔ)漏, 不過(guò),c++對(duì)我來(lái)說(shuō), 已經(jīng)5年沒(méi)有用過(guò)了, 這個(gè)缺很大, 也差不多相當(dāng)重學(xué)了, 所以接下來(lái)的時(shí)間, 重溫一遍啦 ?
資料參考主要是C語(yǔ)言中文網(wǎng)和光城哥寫(xiě)的C++教程,然后再加自己的理解和編程實(shí)驗(yàn)作為輔助,加深印象。
今天這篇文章是C++非常重要的一塊,關(guān)于類(lèi)的繼承和派生,我們知道C++面向?qū)ο箝_(kāi)發(fā)有四大特性: 抽象,封裝,繼承和多態(tài)。 前面發(fā)現(xiàn),通過(guò)定義類(lèi),把事物的數(shù)據(jù)和功能進(jìn)行抽象,而通過(guò)隱藏對(duì)象的屬性和實(shí)現(xiàn)細(xì)節(jié),對(duì)外只提供接口的方式對(duì)類(lèi)的內(nèi)部成員形成了封裝。 這兩個(gè)前面都已經(jīng)了解過(guò), 而這篇文章主要是整理繼承,即子類(lèi)繼承父類(lèi)的特征和行為,使得子類(lèi)具有父類(lèi)的成員變量和方法, 繼承最大的一個(gè)好處就是代碼復(fù)用,兩個(gè)類(lèi)有一些相同的屬性和方法。
這篇內(nèi)容會(huì)有些偏多,還是各取所需即可 ?
主要內(nèi)容如下:
Ok, let’s go!
在聊C++繼承和派生之前,先來(lái)看看C++面向?qū)ο箝_(kāi)發(fā)的四大特性,這樣能先宏觀把握一下繼承到底位于什么樣的位置。
C++面向?qū)ο箝_(kāi)發(fā)有四大特性: 抽象,封裝,繼承和多態(tài), 正所謂編程語(yǔ)言的背后都非常相似,Java既然也是面向?qū)ο蟮恼Z(yǔ)言,同樣也會(huì)有這四大特性。
抽象和封裝前面其實(shí)已經(jīng)整理過(guò)了, 封裝主要講的是信息隱藏,保護(hù)數(shù)據(jù),而抽象又可以從兩個(gè)層面來(lái)理解。
有了上面的宏觀把握,再看繼承就比較容易理解, 簡(jiǎn)單的講,繼承就是一個(gè)類(lèi)從另一個(gè)類(lèi)獲取成員變量和成員函數(shù)的過(guò)程。 此時(shí),被繼承的類(lèi)稱(chēng)為父類(lèi)或基類(lèi),而繼承的類(lèi)稱(chēng)為子類(lèi)或派生類(lèi)。
C++中派生和繼承是站在不同角度看的同種概念。繼承時(shí)從兒子的角度看,派生是父親的角度看,實(shí)際說(shuō)的是一回事。
派生類(lèi)除了擁有他爹的成員,還可以定義自己的新成員,增強(qiáng)功能,此時(shí)的好處就是只需要定義新成員即可,老的成員和功能,直接繼承,實(shí)現(xiàn)了代碼復(fù)用。
下面是兩種典型使用繼承的場(chǎng)景:
繼承的語(yǔ)法:
class 派生類(lèi)名:[繼承方式] 基類(lèi)名{ 派生類(lèi)新增加的成員};
直接看個(gè)栗子:
class People{public: void setname(string name); string getname();private: string m_name; int m_age;};void People::setname(string name){m_name = name;} string People::getname(){return m_name;}class Student: public People{public: void setage(int age); int getage();private: int m_age;};void Student::setage(int age){m_age = age;}int Student::getage(){return m_age;}int main(){ Student stu; stu.setname("zhongqiang"); stu.setage(25); cout << stu.getname() << "的年齡是" << stu.getage() << endl; return 0;}
這個(gè)例子比較簡(jiǎn)單,不解釋?zhuān)?這里就會(huì)發(fā)現(xiàn), Student繼承了People之后,就有他的setname()
和getname()
方法,在子類(lèi)里面可以直接調(diào)用。
上面演示了public的繼承方式,但繼承方式其實(shí)有3種, public, private, protected, 這哥仨不僅可以修飾類(lèi)的成員,還可以指定繼承方式。如果不寫(xiě),默認(rèn)是private(成員變量和成員函數(shù)默認(rèn)也是private), 那么這三種繼承方式到底有啥區(qū)別呢?
public, private, protected這哥仨,可以修飾類(lèi)成員,之前見(jiàn)識(shí)過(guò)public和private了, 這里加上protected之后統(tǒng)一整理下訪問(wèn)權(quán)限的問(wèn)題。
類(lèi)成員的訪問(wèn)權(quán)限從高到低依次是public --> protected --> private
。 public成員可以通過(guò)對(duì)象來(lái)訪問(wèn), private成員不能通過(guò)對(duì)象訪問(wèn), protected成員和private成員蕾西, 也不能通過(guò)對(duì)象訪問(wèn)。
But, 如果存在繼承關(guān)系的時(shí)候, protected和private就不一樣了: 基類(lèi)中的protected成員可以在派生類(lèi)中使用,但是基類(lèi)中的private成員不能再派生類(lèi)中使用。
不同的繼承方式使得基類(lèi)成員在派生類(lèi)中的訪問(wèn)權(quán)限也不一樣, 下面這個(gè)很重要:
使用方法:
下面通過(guò)上面的代碼例子來(lái)演示下, 由于private和protect繼承方式會(huì)改變基類(lèi)成員在派生類(lèi)中的訪問(wèn)權(quán)限,導(dǎo)致繼承關(guān)系復(fù)雜, 所以實(shí)際開(kāi)發(fā)中一般使用public。
把上面的栗子修改下, 測(cè)試下上面的這幾種情況,方便理解,這里只看public繼承下面的。
class People{public: void setname(string name); string getname(); void setage(int age); int getage(); void setsex(string sex); string getsex(); void setwork(string work); string getwork(); // 屬性 string m_sex; protected: string m_work; private: string m_name; int m_age;};void People::setname(string name){m_name = name;} string People::getname(){return m_name;}void People::setage(int age){m_age = age;}int People::getage(){return m_age;}void People::setsex(string sex){m_sex=sex;}string People::getsex(){return m_sex;}void People::setwork(string work){m_work=work;}string People::getwork(){return m_work;}class Student: public People{public: void setscore(float score); float getscore(); // 定義問(wèn)候方法,這里面會(huì)訪問(wèn)基類(lèi)的私有屬性 string helloname(); string hellowork(); string hellosex();private: float m_score;};void Student::setscore(float score){m_score = score;}float Student::getscore(){return m_score;}// 訪問(wèn)基類(lèi)中的公有屬性string Student::hellosex(){return "hello, " + m_sex;}// 訪問(wèn)基類(lèi)中的protect屬性string Student::hellowork(){return "hello, " + m_work;}// 訪問(wèn)基類(lèi)中的私有屬性// string Student::helloname(){return "hello, " + m_name;} error: "std::string People::m_name" is private within this contextint main(){ Student stu; stu.setname("zhongqiang"); stu.setsex("man"); stu.setwork("student"); stu.setage(25); stu.setscore(66.6); cout << stu.getname() << "今年" << stu.getage() << ",性別: " << stu.getsex() << ", 職業(yè): " << stu.getwork() << ", 分?jǐn)?shù): " << stu.getscore() << endl; //cout << stu.helloname() << endl; cout << stu.hellowork() << endl; cout << stu.hellosex() << endl; // 直接通過(guò)對(duì)象訪問(wèn)屬性 cout << stu.m_sex << endl; // 公有屬性到子類(lèi)中依然是公有, 可以被訪問(wèn) //cout << stu.m_name << endl; // error "std::string People::m_name" is private within this context //cout << stu.m_work << endl; // error "std::string People::m_work" is protected within this context //cout << stu.m_score << endl; // error "float Student::m_score" is private within this context return 0;}
在這里面就可以看出來(lái), 在Student里面的成員函數(shù)中,只能訪問(wèn)到他爹的public屬性和protect屬性,不能訪問(wèn)他爹的private屬性。而如果是通過(guò)Student的對(duì)象, 那么只能訪問(wèn)public屬性,protect和private的都訪問(wèn)不到。
在派生類(lèi)中訪問(wèn)基類(lèi)的private成員的唯一方法就是借助基類(lèi)的非private成員函數(shù),如果基類(lèi)沒(méi)有非private成員函數(shù),那么該成員在派生類(lèi)中將無(wú)法訪問(wèn)。
這里注意一個(gè)問(wèn)題,這里說(shuō)的是基類(lèi)的 private 成員不能在派生類(lèi)中使用,并不是說(shuō)基類(lèi)的 private 成員不能被繼承。實(shí)際上,基類(lèi)的 private 成員是能夠被繼承的,并且(成員變量)會(huì)占用派生類(lèi)對(duì)象的內(nèi)存,它只是在派生類(lèi)中不可見(jiàn),導(dǎo)致無(wú)法使用罷了。private 成員的這種特性,能夠很好的對(duì)派生類(lèi)隱藏基類(lèi)的實(shí)現(xiàn),以體現(xiàn)面向?qū)ο蟮姆庋b性。
using關(guān)鍵字可以改變基類(lèi)成員在派生類(lèi)中的訪問(wèn)權(quán)限, 比如將public改成private, protected改成public。
注意:using 只能改變基類(lèi)中 public 和 protected 成員的訪問(wèn)權(quán)限,不能改變 private 成員的訪問(wèn)權(quán)限,因?yàn)榛?lèi)中 private 成員在派生類(lèi)中是不可見(jiàn)的,根本不能使用,所以基類(lèi)中的 private 成員在派生類(lèi)中無(wú)論如何都不能訪問(wèn)
class People{public: void setname(string name); string getname(); void setage(int age); int getage(); void setsex(string sex); string getsex(); void setwork(string work); string getwork(); // 屬性 string m_sex; protected: string m_work; private: string m_name; int m_age;};void People::setname(string name){m_name = name;} string People::getname(){return m_name;}void People::setage(int age){m_age = age;}int People::getage(){return m_age;}void People::setsex(string sex){m_sex=sex;}string People::getsex(){return m_sex;}void People::setwork(string work){m_work=work;}string People::getwork(){return m_work;}class Student: public People{public: void setscore(float score); float getscore(); // 定義問(wèn)候方法,這里面會(huì)訪問(wèn)基類(lèi)的私有屬性 string helloname(); string hellowork(); string hellosex(); using People::m_work; // 將m_work提升成public權(quán)限 private: float m_score; using People::m_sex; // 將m_sex降低為private權(quán)限};void Student::setscore(float score){m_score = score;}float Student::getscore(){return m_score;}// 訪問(wèn)基類(lèi)中的公有屬性string Student::hellosex(){return "hello, " + m_sex;}// 訪問(wèn)基類(lèi)中的protect屬性string Student::hellowork(){return "hello, " + m_work;}// 訪問(wèn)基類(lèi)中的私有屬性// string Student::helloname(){return "hello, " + m_name;} error: "std::string People::m_name" is private within this contextint main(){ Student stu; stu.setname("zhongqiang"); stu.setsex("man"); stu.setwork("student"); stu.setage(25); stu.setscore(66.6); cout << stu.getname() << "今年" << stu.getage() << ",性別: " << stu.getsex() << ", 職業(yè): " << stu.getwork() << ", 分?jǐn)?shù): " << stu.getscore() << endl; // 直接通過(guò)對(duì)象訪問(wèn)屬性 //cout << stu.m_sex << endl; // 這個(gè)這時(shí)候就會(huì)報(bào)錯(cuò)了 cout << stu.m_work << endl; // 這個(gè)就可以訪問(wèn)了 return 0;}
注意,using修改的是派生類(lèi)里面的成員訪問(wèn)權(quán)限。并且是只能修改public和protected的訪問(wèn)權(quán)限。
這個(gè)說(shuō)的情況是派生類(lèi)中的成員(變量和函數(shù)),如果和基類(lèi)中的成員重名,那么在派生類(lèi)中使用該成員,實(shí)際上用的是派生類(lèi)新增的成員,而不是從基類(lèi)繼承過(guò)來(lái)的。 即派生類(lèi)遮蔽掉從基類(lèi)繼承過(guò)來(lái)的成員。
下面的這個(gè)例子,是Student繼承了People, 又重寫(xiě)了People的show函數(shù),那么通過(guò)Student對(duì)象調(diào)用show的時(shí)候,實(shí)際上是用的Student自身的show函數(shù),但People的show函數(shù)也被Student繼承了過(guò)來(lái),如果想用,需要加上類(lèi)名和域解析符。
class People{public: void show();protected: string m_name; int m_age;};void People::show(){ cout << m_name << " " << m_age << endl; }class Student: public People{public: Student(string name, int age, string sex); void show(); // 遮蔽基類(lèi)的show() private: string m_sex;};Student::Student(string name, int age, string sex): m_sex(sex){ m_name = name; m_age = age; //m_sex = sex;}void Student::show(){ cout << m_name << " " << m_age << " " << m_sex << endl;}int main(){ Student stu("zhongqiang", 25, "man"); // 派生類(lèi)新增的成員函數(shù) stu.show(); // zhongqiang 25 man // 使用從基類(lèi)繼承過(guò)來(lái)的成員函數(shù) stu.People::show(); // zhongqiang 25 return 0;}
這里我在實(shí)驗(yàn)的時(shí)候,發(fā)現(xiàn)個(gè)問(wèn)題,就是Student的構(gòu)造函數(shù)定義的時(shí)候, 本來(lái)是想用構(gòu)造函數(shù)初始化列表的方式,一開(kāi)始寫(xiě)的代碼是這樣:
Student::Student(string name, int age, string sex): m_name(name), m_age(age){ m_sex = sex;}
此時(shí)編譯錯(cuò)誤, 報(bào)錯(cuò)原因class "Student" does not have any field named "m_name"
, 而如果寫(xiě)成上面那種形式,或者不用參數(shù)化列表的方式,就沒(méi)問(wèn)題, 所以這里我感覺(jué),參數(shù)化列表那個(gè)地方的參數(shù),應(yīng)該是當(dāng)前類(lèi)具有的成員變量才行, 繼承過(guò)來(lái)的應(yīng)該是不能往這里寫(xiě)。
上面的例子,其實(shí)就是派生類(lèi)對(duì)基類(lèi)的函數(shù)重寫(xiě),內(nèi)部在執(zhí)行的時(shí)候, 先找派生類(lèi)里面有沒(méi)有對(duì)應(yīng)的函數(shù),如果有,就先執(zhí)行派生類(lèi)里面的重名函數(shù),如果沒(méi)有,那么再執(zhí)行基類(lèi)里面定義的。
那么,如果派生類(lèi)里面的函數(shù)和基類(lèi)的函數(shù)重名,但形參列表不一樣的時(shí)候,此時(shí)會(huì)發(fā)生重載現(xiàn)象嗎? 答: 不會(huì)。 一旦派生類(lèi)中有同名函數(shù),不管他們的參數(shù)是否一樣,都會(huì)把基類(lèi)中所有的同名函數(shù)遮蔽掉。
這個(gè)就不用例子演示了,而是整理下背后的所以然吧。
之前整理過(guò),每個(gè)類(lèi)都會(huì)有自己的作用域, 在這個(gè)作用域內(nèi)會(huì)定義類(lèi)的成員,那么,當(dāng)存在繼承關(guān)系的時(shí)候, 派生類(lèi)的作用域嵌套在基類(lèi)的作用域之內(nèi),如果一個(gè)名字在派生類(lèi)的作用域沒(méi)有找到,編譯器會(huì)繼續(xù)到外層的基類(lèi)作用域查找該名字的定義。
兩條:
看個(gè)嵌套作用域的例子:
class A{public: void func();public: int n = 500;};void A::func(){ cout<<"hello, changjinhu!!!"<<endl; }class B: public A{public: int n = 5000; int m;};class C: public B{public: int n = 50000; int x;};int main(){ C obj; cout << obj.n << endl; obj.func(); cout<<sizeof(C)<<endl; return 0;}
這個(gè)例子中的繼承關(guān)系, B繼承A, C繼承B,那么作用域的嵌套關(guān)系如下:
func()
的時(shí)候,編譯器沒(méi)有在C類(lèi)里面找到func這個(gè)名字,會(huì)繼續(xù)到B作用域找,也沒(méi)有找到,再往外,從A里面找到了,于是,調(diào)用A類(lèi)作用域的func()
函數(shù)。有了上面這些,就能回答上面的兩點(diǎn)疑問(wèn):
沒(méi)有繼承時(shí)對(duì)象內(nèi)存的分布情況,成員變量和成員函數(shù)分開(kāi)存儲(chǔ):
有繼承關(guān)系的時(shí)候, 派生類(lèi)的內(nèi)存模型可以看成是基類(lèi)成員變量和新增成員變量的總和,所有成員函數(shù)仍然存儲(chǔ)在另外一個(gè)區(qū)域–代碼區(qū),由所有對(duì)象共享。
看個(gè)例子:
class A{public: A(int a, int b);protected: int m_a; int m_b;};A::A(int a, int b): m_a(a), m_b(b){}class B
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/122560.html
摘要:注意在完成配置環(huán)境變量后測(cè)試是否安裝成功時(shí)鍵入命令安裝出現(xiàn)了這樣的問(wèn)題,需要升級(jí)具體安裝方法,可以參考該文檔教程下載最新的之后,上邊的問(wèn)題就解決了。 由于其他項(xiàng)目中要使用Java的項(xiàng)目,所以,簡(jiǎn)單的學(xué)下,好對(duì)項(xiàng)目有個(gè)大概的了解。 一、Eclipse 安裝 1.下載地址為: https://www.eclipse.org/downl... 2.配置環(huán)境 在配置環(huán)境變量中:設(shè)置JAVA_H...
摘要:基類(lèi)中的構(gòu)造函數(shù)和析構(gòu)函數(shù)不能被繼承,在派生類(lèi)中需要定義新的構(gòu)造函數(shù)和析構(gòu)函數(shù),私有成員不能被繼承。對(duì)象訪問(wèn)在派生類(lèi)外部,通過(guò)派生類(lèi)的對(duì)象對(duì)從基類(lèi)繼承來(lái)的成員的訪問(wèn)。 ...
摘要:類(lèi)的介紹類(lèi)用來(lái)描述具有相同的屬性和方法的對(duì)象的集合。類(lèi)變量類(lèi)變量在整個(gè)實(shí)例化的對(duì)象中是公用的。類(lèi)的定義語(yǔ)法格式如下類(lèi)有一個(gè)名為的特殊方法,也即是構(gòu)造函數(shù),該方法會(huì)在定義對(duì)象的時(shí)候自動(dòng)調(diào)用,可以通過(guò)參數(shù)傳遞來(lái)對(duì)類(lèi)的實(shí)例進(jìn)行設(shè)定。 1. 類(lèi)的介紹 類(lèi)(Class) 用來(lái)描述具有相同的屬性和方法的對(duì)象的集合。它定義了該集合中每個(gè)對(duì)象所共有的屬性和方法。對(duì)象是類(lèi)的實(shí)例,類(lèi)是對(duì)象的抽象。 ...
閱讀 1626·2021-10-14 09:43
閱讀 5503·2021-09-07 10:21
閱讀 1275·2019-08-30 15:56
閱讀 2123·2019-08-30 15:53
閱讀 1231·2019-08-30 15:44
閱讀 2010·2019-08-30 15:44
閱讀 1320·2019-08-29 17:24
閱讀 752·2019-08-29 15:19