c++引用類型(c++引用的定義)

發(fā)布時(shí)間:2024-03-20
本文主要介紹c引用類型(c引用的定義),下面一起看看c引用類型(c引用的定義)相關(guān)資訊。
-
引用、繼承和派生引用的介紹首先要知道,參數(shù)的傳遞本質(zhì)上是一個(gè)賦值的過程,賦值就是復(fù)制記憶。所謂內(nèi)存復(fù)制是指將數(shù)據(jù)從一個(gè)內(nèi)存復(fù)制到另一個(gè)內(nèi)存,對(duì)于聚合類型(復(fù)雜類型,如結(jié)構(gòu)和類)可能會(huì)消耗大量?jī)?nèi)存。
一個(gè)引用可以看作是一個(gè)數(shù)據(jù)的別名,通過這個(gè)別名和原來的名字(指向同一個(gè)內(nèi)存)可以找到這個(gè)數(shù)據(jù)。
注意:
引用必須在定義的同時(shí)初始化,以后會(huì)一直使用,不能引用其他數(shù)據(jù)。這有點(diǎn)類似于常量(const variable)引用,定義的時(shí)候需要添加,使用的時(shí)候不能添加。使用的時(shí)候,加法就是取地址int a = 99int r = a;cout a , r endl一般來說,在c中,引用作為函數(shù)參數(shù)來代替指針的作用,同樣可以改變數(shù)據(jù)內(nèi)容,非常實(shí)用。
同時(shí),在c中,引用可以作為函數(shù)返回值,但是?。?!對(duì)本地?cái)?shù)據(jù)(如本地變量、本地對(duì)象、本地?cái)?shù)組等)的引用。)無法返回,因?yàn)楹瘮?shù)調(diào)用完成后本地?cái)?shù)據(jù)會(huì)被破壞,下次數(shù)據(jù)可能就不存在了。
int plus 10(int r){ int m = r 10;返回m;//返回對(duì)本地?cái)?shù)據(jù)的引用}//對(duì)于某些編譯器,會(huì)報(bào)錯(cuò)int num 3 = plus 10(num 1);int num 4 = plus 10(num 3);//但是有些編譯器是可以運(yùn)行的,比如gcc,但是num3和num4的值是一樣的,因?yàn)楹瘮?shù)是在棧上運(yùn)行的,運(yùn)行之后會(huì)放棄所有本地?cái)?shù)據(jù)的管理權(quán),后面的函數(shù)調(diào)用會(huì)覆蓋前面函數(shù)的本地?cái)?shù)據(jù),這兩點(diǎn)會(huì)改成最后一個(gè)值;語錄的精髓:
其實(shí)引用只是指針的簡(jiǎn)單封裝,它的底層還是通過指針來實(shí)現(xiàn)的。引用占用的內(nèi)存和指針占用的內(nèi)存是一樣的,32位環(huán)境下是4字節(jié),64位環(huán)境下是8字節(jié)。之所以無法獲得引用的地址,是因?yàn)榫幾g器進(jìn)行了內(nèi)部轉(zhuǎn)換:
int a = 99int r = a;r = 18coutrendl//在編譯時(shí),會(huì)轉(zhuǎn)換成如下形式:int a = 99int * r = a;* r = 18coutrendl當(dāng)r取地址時(shí),編譯器會(huì)隱式轉(zhuǎn)換代碼,使代碼輸出r的內(nèi)容(a的地址)而不是r的地址,這也是無法獲取引用的原因。變量地址的原因。也就是說,不是變量r不占用內(nèi)存,而是編譯器不允許它獲取自己的地址。
指針和引用之間的其他區(qū)別:
引用必須在定義的時(shí)候初始化,以后也一直如此,不能再指向其他數(shù)據(jù);指針不 我沒有這個(gè)限制。指針不 它們?cè)诙x時(shí)不必賦值,將來可以指向任意數(shù)據(jù)。
你可以有常量指針,但是沒有常量引用,r可以 不要改變重點(diǎn),添加常量是不必要的。
指針可以有多級(jí),引用只能有一級(jí)(如果你學(xué)了引用折疊,你可以想想它是不是正確的)。比如int **p是合法的,int r是非法的(c 11加了右值引用,是合法的),下面這個(gè)就可以了。
int a = 10int r = a;int rr = r;//指向a的兩個(gè)地址指針與引用的遞增和遞減(-)操作的含義不同。使用指針表示它指向下一個(gè)數(shù)據(jù),使用引用表示它所引用的數(shù)據(jù)本身加1。
引用通常不能綁定臨時(shí)數(shù)據(jù):
指針和引用只能指向內(nèi)存,不能指向寄存器或硬盤,因?yàn)榧拇嫫骱陀脖P是不能尋址的。
定義的變量、創(chuàng)建的對(duì)象、字符串常量、函數(shù)參數(shù)、函數(shù)體本身、new或malloc分配的內(nèi)存等。,所有這些都可以用來獲取地址。
什么數(shù)據(jù)可以 t被使用,它將被記錄在:
基本類型的數(shù)據(jù),如int、double、bool、char,通常小于8個(gè)字節(jié),可以存放在一兩個(gè)寄存器中,所以這些類型的臨時(shí)數(shù)據(jù)通常放在寄存器中;但是,對(duì)象和結(jié)構(gòu)變量是用戶定義的數(shù)據(jù)類型,它們的大小是不可預(yù)測(cè)的,所以這些類型的臨時(shí)數(shù)據(jù)通常放在內(nèi)存中。int * p2 =(n 100);//不會(huì),n 100會(huì)在寄存器里,常量表達(dá)式也會(huì)在寄存器里;s1 = {23,45 };s s2 = {90,75 };s * p1 =(s1 s2);//在visual c中是,s1 s2在內(nèi)存中,但是!?。?!gcc can t,因?yàn)間cc可以 不要引用任何臨時(shí)變量!??!bool iso dd(int n){ if(n % 2 = = 0){ return false;}else{返回true} } iso dd(a);//更正iso dd(a9);//錯(cuò)誤,有時(shí)它 向它傳遞臨時(shí)數(shù)據(jù)很容易。去看看吧
常量引用綁定臨時(shí)數(shù)據(jù):
常量引用:編譯器會(huì)為臨時(shí)數(shù)據(jù)創(chuàng)建一個(gè)新的,沒有名字的臨時(shí)變量,把臨時(shí)數(shù)據(jù)放到臨時(shí)變量里,然后把引用綁定到臨時(shí)變量上。
把它改成常規(guī)引用就行了,因?yàn)闉槌R?guī)引用創(chuàng)建臨時(shí)變量是沒有意義的。創(chuàng)建和修改的臨時(shí)變量只是臨時(shí)變量中的數(shù)據(jù),不會(huì)影響原始數(shù)據(jù),意義不大。對(duì)于頻繁引用,我們只能通過const引用讀取數(shù)據(jù)的值,但是可以 t修改它的值,所以我們不 不用考慮同步更新的問題,我們不會(huì) t產(chǎn)生兩個(gè)不同的數(shù)據(jù)。
bool isodd(const int n){ //更改為頻繁引用if(n/2 = = 0){ r《整數(shù)在內(nèi)存中是如何存儲(chǔ)的》《小數(shù)在內(nèi)存中是如何存儲(chǔ)的》);因?yàn)橐玫谋举|(zhì)也是指針,所以引用的類型轉(zhuǎn)換也是錯(cuò)誤的。
int n = 100int * p1 = n;//正確的float * p2 = n;//error int r1 = n;//修正浮點(diǎn)r2 = n;//錯(cuò)了但是!??!通過添加頻繁引用可以進(jìn)行類型轉(zhuǎn)換。
原則:當(dāng)引用和數(shù)據(jù)的類型不一致時(shí),如果它們的類型相似,則 數(shù)據(jù)類型的自動(dòng)轉(zhuǎn)換,那么編譯器會(huì)創(chuàng)建一個(gè)臨時(shí)變量,將數(shù)據(jù)賦給這個(gè)臨時(shí)變量(此時(shí)會(huì)發(fā)生自動(dòng)類型轉(zhuǎn)換),然后將引用綁定到這個(gè)臨時(shí)變量,這與 綁定對(duì)臨時(shí)數(shù)據(jù)的常量引用。
int n = 100int r1 = n;//正確的常量float r2 = n;//正確總結(jié):如果函數(shù)不要求改變引用值,函數(shù)參數(shù)應(yīng)該使用const盡可能;第一,避免臨時(shí)數(shù)據(jù);第二,避免不同類型;第三,常量和非常量參數(shù)都是可以接受的;
double volume(const double len,const double width,const double hei){ return len * width * 2 len * hei * 2 width * hei * 2;}double v4 = volume(a 12.5,b 23.4,16.78);double v5 =音量(a b,a c,b c);繼承和派生繼承介紹了被繼承的類稱為父類或基類,被繼承的類稱為子類或派生類。 子類 和 父類和通常被召集在一起,而 基類和和 派生類 通常被召集在一起。意思是一樣的。
什么時(shí)候?等待繼承:類之間必須有很大的相關(guān)性,有很多共同的成員函數(shù)和成員變量。
繼承的成員可以通過子類對(duì)象訪問,就像它們自己的一樣。
繼承格式:
類派生類名稱:[繼承方法]基類名稱{派生類新添加的成員};
三種繼承
公共繼承模式
基類中的公共成員-派生類中成為基類中公共屬性的受保護(hù)成員-派生類中的私有成員或基類中的受保護(hù)屬性-不能在派生類中使用的不可見的受保護(hù)繼承方法。
基類中的公共成員——派生類中的受保護(hù)成員成為基類中的受保護(hù)成員——派生類中的私有成員或基類中的受保護(hù)成員——派生類中不能使用的不可見私有繼承方法。
在基類中,public\protected-成為派生類中的私有屬性。在派生類中,只有基類中的私有成員才能在類中使用——不能在派生類中使用,不可見的protected屬性只能在派生類(類代碼)中訪問;沒別的;
不難發(fā)現(xiàn):
1)繼承模式中的public、protected、private是用來指明基類成員在派生類中的最高訪問權(quán)限,不能超過,即即使基類是公共成員屬性,派生類采用受保護(hù)的繼承,公共成員屬性也只能變成受保護(hù)的;
2)基類中的私有成員永遠(yuǎn)不能在派生類中使用,但是可以通過public的set和get函數(shù)使用(訪問派生類中私有成員的唯一方法是使用基類的非私有成員函數(shù))。
3)如果希望基類的成員被派生類繼承,無障礙使用,那么這些成員只能聲明為public或者protected。
4)如果您希望基類的成員不公開(不能通過對(duì)象訪問),但也在派生類中使用,則只能將它們聲明為protected。
注意,我們這里說的是基類的私有成員不能在派生類中使用,而不是基類的私有成員不能被繼承。其實(shí)基類的私有成員是可以繼承的,(成員變量)會(huì)占用派生類對(duì)象的內(nèi)存,但是在派生類中是不可見的,這就使得它不可用。
若要更改訪問權(quán)限,請(qǐng)使用using關(guān)鍵字。
使用只能更改基類中公共和受保護(hù)成員的訪問權(quán)限,而不能更改私有成員的訪問權(quán)限。因?yàn)榛愔械乃接谐蓡T在派生類中是不可見的,不能被寫入;
//基類people class people {public: void show;protect: char * m _ name;int m _ age};虛空人: :秀{ cout m _ nam:公人{(lán)public: void learning;public:使用people ::m _ name;//將protected改為public使用人: : _ age;//將protected改為public float m _ scor:用people :: show;//將public改為private };繼承時(shí)的名稱屏蔽
對(duì)于函數(shù)來說,無論函數(shù)的參數(shù)是什么,只要名稱相同,就會(huì)被遮擋,不會(huì)出現(xiàn)重載。如果要調(diào)用,使用域名和域解析器;
對(duì)于成員變量,只要名稱相同,派生類就會(huì)覆蓋基類,但是基類的成員變量同時(shí)存在。此時(shí),從基類繼承的get和set方法都是對(duì)基類中同名變量的操作,而不是對(duì)派生類中同名變量的操作。
類繼承的作用域嵌套和對(duì)象內(nèi)存模型假設(shè)base是基類,derived是派生類,那么它們的作用域嵌套關(guān)系將如下:
編譯器將從內(nèi)向外查找以下類的范圍:
通過obj (c類對(duì)象c)訪問成員變量n時(shí),可以在c類的作用域中找到n這個(gè)名字,雖然a類和b類都有n這個(gè)名字,但是編譯器不會(huì)在它們的作用域中查找,所以是不可見的,也就是派生類中的n遮住了基類中的n。
通過obj訪問成員函數(shù)func時(shí),在c類的作用域中沒有找到名字func,b也沒有 我找不到它。然后我們繼續(xù)在a類的范圍內(nèi)搜索,結(jié)果找到了func這個(gè)名字。當(dāng)搜索結(jié)束時(shí),編譯器決定調(diào)用類a來做這件事。在域中使用func函數(shù)(這個(gè)過程叫做名稱搜索,而且都是通過名稱搜索,除非直接通過域名和域解析器搜索,否則不會(huì)有這個(gè)過程);
對(duì)象內(nèi)存模型:
沒有繼承的時(shí)候,簡(jiǎn)單,變量存在于堆或者棧區(qū),函數(shù)存在于代碼段;
當(dāng)繼承存在時(shí):
所有變量都連續(xù)存在于堆區(qū)或棧區(qū)(成員變量按照派生的層次順序排列,新加入的成員變量總是在最后,私有和隱藏變量也會(huì)在內(nèi)存中),函數(shù)存在于代碼區(qū)(所有對(duì)象都是共享的,但能否使用取決于權(quán)限,私有的可以 必要時(shí)不使用)。
示例:
obj_a是基類對(duì)象,obj_b是派生類對(duì)象。假設(shè)obj_a的起始地址為0x1000,其內(nèi)存分布如下圖所示:
假設(shè)obj_b的起始地址是0x1100,a類中的m_b是私有的,那么它的內(nèi)存分布如下圖所示:
假設(shè)obj_c的起始地址是0x1300,并且有屏蔽,其內(nèi)存分布如下圖所示:
總結(jié):在派生類的對(duì)象模型中,會(huì)包含基類的所有成員變量。這種設(shè)計(jì)方案的優(yōu)點(diǎn)是訪問效率高,可以在派生類對(duì)象中直接訪問基類變量,不需要幾層間接計(jì)算。
在設(shè)計(jì)基類和派生類的構(gòu)造函數(shù)/析構(gòu)函數(shù)時(shí),派生類的構(gòu)造函數(shù)也要對(duì)繼承的成員變量進(jìn)行初始化,但大多數(shù)基類都有帶有私有屬性的成員變量,在派生類中是的,更不用說被派生類的構(gòu)造函數(shù)初始化了。解決這個(gè)問題的思路是在派生類的構(gòu)造函數(shù)中調(diào)用基類的構(gòu)造函數(shù)。
# includeiostreamusing命名空間std//基類people class people{protect: char * m _ name;int m _ agepublic:人(char*,int);};人: :人(char * nam: m _ nam:公有p: float m _ score;public:學(xué)生(char *name,int age,float score);空白display;};//people(姓名,年齡)是調(diào)用基類的構(gòu)造函數(shù),student : : student(char * name,intage,float scor: people(姓名,年齡),m_score(分?jǐn)?shù)){ } void student : : display{ coutm _ name 有著 的年齡年齡 得分為 m _ score 。 endl} int main{ student stu( 小明 , 16, 90.5);stu.display。返回0;}people(name,age)是調(diào)用基類的構(gòu)造函數(shù)并將名字和年齡作為實(shí)參傳遞給它,m_score(score)是派生類的參數(shù)初始化表。其次,不存在m_score(score)放在前面的問題,它會(huì)遵循先調(diào)用基類構(gòu)造函數(shù),再初始化參數(shù)初始化表中其他成員變量的原則。
構(gòu)造函數(shù)調(diào)用順序:
當(dāng)類a-b-c按照a類構(gòu)造函數(shù)-b類構(gòu)造函數(shù)-c類構(gòu)造函數(shù)的順序執(zhí)行時(shí),其中a是c的間接基類,b是c的直接基類;派生類的構(gòu)造函數(shù)只能調(diào)用直接基類的構(gòu)造函數(shù),而不能調(diào)用間接基類的構(gòu)造函數(shù),因?yàn)閏丟棄了b類的構(gòu)造函數(shù),b會(huì)先調(diào)用a類的構(gòu)造函數(shù),相當(dāng)于c間接(或隱式)調(diào)用a的構(gòu)造函數(shù),如果在c中再次顯式調(diào)用a的構(gòu)造函數(shù),a的構(gòu)造函數(shù)就會(huì)被調(diào)用兩次,相應(yīng)的,初始化工作也會(huì)做兩次,不僅多余,而且沒有必要。
基類構(gòu)造函數(shù)調(diào)用規(guī)則:
通過派生類創(chuàng)建對(duì)象時(shí),必須調(diào)用基類的構(gòu)造函數(shù),這是語法規(guī)則。換句話說,在定義派生類構(gòu)造函數(shù)時(shí),最好參考明基類構(gòu)造函數(shù);如果沒有指定,調(diào)用基類的默認(rèn)構(gòu)造函數(shù)(不帶參數(shù)的構(gòu)造函數(shù));如果沒有默認(rèn)構(gòu)造函數(shù)。
#使用命名空間std包含iostreamusing//基類people class people{public:人;//基類的默認(rèn)構(gòu)造函數(shù)people(char *name,int age);protect: char * m _ nam::p: m _ name( xxx ),m _ age(0){ } people ::people(char * nam: m _ nam:公立people{public:學(xué)生;student(char*,int,float);public:虛空顯示;privat:浮點(diǎn)m _ scor::stud: m _ score(0.0){ }//派生類默認(rèn)構(gòu)造函數(shù)student: :學(xué)生(char * name,intage,float scor:人(姓名,年齡),m _ score(分?jǐn)?shù)){ } void student: : display{ coutm _ name 有著 的年齡年齡 得分為 m _ score 。 endl} int main{ student stu 1;stu 1 . display;學(xué)生stu 2( 小明 , 16, 90.5);stu 2 . display;返回0;}在創(chuàng)建對(duì)象stu1時(shí),執(zhí)行派生類的構(gòu)造函數(shù)student: : student,該函數(shù)不指定調(diào)用基類的哪個(gè)構(gòu)造函數(shù)。從運(yùn)行結(jié)果中可以明顯看出,系統(tǒng)默認(rèn)調(diào)用不帶參數(shù)的構(gòu)造函數(shù),即people : :人。
創(chuàng)建對(duì)象stu2時(shí),執(zhí)行派生類的構(gòu)造函數(shù)student: :學(xué)生(char * name,intage,f)。loat score),它指示基類的構(gòu)造函數(shù)。
對(duì)于析構(gòu)函數(shù)
析構(gòu)函數(shù)也不能被繼承。與構(gòu)造函數(shù)不同的是,在派生類的析構(gòu)函數(shù)中不需要顯式調(diào)用基類的析構(gòu)函數(shù),因?yàn)槊總€(gè)類只有一個(gè)析構(gòu)函數(shù),編譯器知道如何選擇,不需要我們的干預(yù)。
析構(gòu)函數(shù)和構(gòu)造函數(shù)的執(zhí)行順序正好相反:
創(chuàng)建派生類對(duì)象時(shí),構(gòu)造函數(shù)的執(zhí)行順序與繼承順序相同,即先執(zhí)行基類構(gòu)造函數(shù),再執(zhí)行派生類構(gòu)造函數(shù)。當(dāng)派生類對(duì)象被銷毀時(shí),析構(gòu)函數(shù)的執(zhí)行順序與繼承順序相反,即先執(zhí)行派生類析構(gòu)函數(shù),再執(zhí)行基類析構(gòu)函數(shù)。多繼承多繼承對(duì)象內(nèi)存模型c不僅有單繼承,還有多繼承。
d:類公共a、私有b、受保護(hù)c {//d類的新成員}
d是一個(gè)具有多種繼承形式的派生類。它以公共繼承a類,以私有繼承b類,以保護(hù)繼承c類。d .根據(jù)不同的繼承方法,得到a、b、c中的成員,確定其在派生類中的訪問權(quán)限。
多重繼承下的構(gòu)造:
基類構(gòu)造函數(shù)的調(diào)用順序與它們?cè)谂缮惖臉?gòu)造函數(shù)中出現(xiàn)的順序無關(guān),但與聲明的派生類的時(shí)基類出現(xiàn)的順序相同(類似于類中的變量)。和上面的例子一樣,先構(gòu)造a,再構(gòu)造b,再構(gòu)造c,最后構(gòu)造d;
命名:
當(dāng)兩個(gè)或多個(gè)基類中存在同名的成員(成員變量或成員函數(shù))時(shí),如果直接訪問該成員,就會(huì)發(fā)生命名,編譯器不知道使用基類的哪個(gè)成員。此時(shí),需要在成員名前添加類名和域解析器::,以便明確指出使用哪個(gè)類成員,消除歧義。
內(nèi)存模型:
直接上例:
#include cstdiousing命名空間std//基類a class a{public: a(int a,int b);protect:國(guó)際機(jī)場(chǎng);int m _ b;};a:: a(int a,int b): m _ a(a),m _ b(b){ }//基類bclass b{public: b(int b,int c);protect: int m _ b;int m _ c;};b::b(intb,int c): m _ b(b),m _ c(c){ }//派生類c c:公共a,公共b{public: c(int a,int b,int c,int d);public:虛空顯示;privat:國(guó)際機(jī)場(chǎng);int m _ c;int m _ d;};c: :c(int a,int b,int c,int d): a(a,b),b(b,c),m_a(a),m_c(c),m _ d(d){ } void c: : display{ printf( a: :m _ a = % d,a: :m _ b = % d \ n ,a::m_a,a: :m _ b);printf( b: :m _ b = % d,b: :m _ c = % d \ n ,b::m_b,b: :m _ c);printf( c: :m _ a = % d,c: :m _ c = % d,c: :m _ d = % d \ n ,c::m_a,c::m_c,m _ d);}int main{ c obj_c(10,20,30,40);obj _ c . display;返回0;}借助指針突破訪問權(quán)限:
認(rèn)為指針指向內(nèi)存地址,對(duì)象指針指向?qū)ο蟮膬?nèi)存地址,通過內(nèi)存模型可以知道private也在連續(xù)內(nèi)存中,所以?。?!就用手指pin偏移量可以強(qiáng)制訪問私有成員變量;例如:
圖中假設(shè)obj對(duì)象的起始地址為0x1000,m_a(public)、m_b(public)和m_c (private)分別與對(duì)象的開頭相隔0、4、8個(gè)字節(jié)。我們稱之為距離偏移。
你知道:
int b = p-m _ b;
會(huì)轉(zhuǎn)換成:int b = *(int *)((int)p sizeof(int));
實(shí)際上是:int b = *(int *)((int)p4);
有:
所以:int c = *(int *)((int)p sizeof(int)* 2);//it ;就這么簡(jiǎn)單。
虛擬繼承1。什么是虛擬繼承和虛擬基類?
多重繼承容易出現(xiàn)命名:經(jīng)典鉆石繼承
第一個(gè)問題:在派生類中保存間接基類成員的多個(gè)副本,雖然不同的數(shù)據(jù)可以存儲(chǔ)在不同的成員變量中,但在大多數(shù)情況下是多余的,因?yàn)楸4娑鄠€(gè)成員變量不僅占用更多的存儲(chǔ)空間,而且容易導(dǎo)致命名。如果a類有一個(gè)成員變量a,那么在d類中直接訪問a會(huì)導(dǎo)致二義性,而編譯器沒有 我不知道它是來自路徑a-b-d還是來自路徑a-c-d
為了消除歧義,我們可以在m_a前面注明來自哪個(gè)類(按域處理)。
但是內(nèi)存中仍然有兩個(gè)間接基類,很消耗內(nèi)存,所以為了解決多重繼承中的命名和冗余數(shù)據(jù)問題,提出了虛擬繼承,使得派生類中只保留間接基類的一個(gè)成員。
//間接基類aclass a{ //虛擬基類protect: int m _ a;};//直接基類b class b:虛公共a {//虛繼承protect: int m _ b;};//直接基類c類c:虛公共a {//虛繼承protect: int m _ c;};//派生類d類d:公共b,公共c{public: void seta(inta){ m _ a = a;}//更正void setb(int b){ m _ b = b;}//更正void setc(int c){ m_c= c;}//更正void s: int m _ d;};int main{ d d;返回0;虛擬繼承的目的是讓一個(gè)類聲明它愿意共享它的基類。其中,這個(gè)共享基類稱為虛擬基類。
我們可以看到一個(gè)問題:虛擬派生的運(yùn)算必須在虛擬派生的真實(shí)需求出現(xiàn)之前完成,也就是在d的需求出現(xiàn)之前,要將b和c設(shè)置為虛擬繼承;
即虛擬派生只影響指定虛擬基類的派生類進(jìn)一步派生的類(繼承bc的類,比如e繼承b,f繼承c會(huì)有影響),不會(huì)影響派生類本身(bc);
在實(shí)際開發(fā)中,中間層的基類一般會(huì)將其繼承聲明為虛擬繼承,不會(huì)帶來任何問題。使用虛擬繼承的類層次結(jié)構(gòu)是由一個(gè)人或一個(gè)項(xiàng)目組一次性設(shè)計(jì)的,因?yàn)闆]有考慮到后期的需求,所以不需要修改中間函數(shù);c庫(kù)iostream只是采用了虛繼承。
2.虛擬基類成員的可見性
以鉆石繼承為例,假設(shè)a定義了一個(gè)名為x的成員變量,當(dāng)我們?cè)赿中直接訪問x時(shí),有三種可能:
如果b和c中都沒有x的定義,那么x會(huì)被解釋為a的成員,此時(shí)不存在歧義。如果b或c中的某個(gè)類定義了x,就不會(huì)有二義性,派生類的x比虛基類的x優(yōu)先級(jí)高。如果x在b和c中都有定義,那么直接訪問x會(huì)造成二義性。第二類問題:bc包含相同優(yōu)先級(jí)的變量。此時(shí),只能使用領(lǐng)域分析來消除歧義。
3.虛擬繼承的構(gòu)造函數(shù)和內(nèi)存模型;
與繼承的構(gòu)造過程不同,派生類的構(gòu)造函數(shù)必須調(diào)用虛基類的構(gòu)造函數(shù)。
#使用命名空間std包含iostreamusing//虛擬基類a class a{public: a(int a);protect:國(guó)際機(jī)場(chǎng);};a: : a(int a): m _ a(a){ }//直接從b class b:虛擬公共a {公共echo。2-@.com b(int a,int b);public:虛空顯示;protect: int m _ b;};b: :b(int a,int b): a(a),m _ b(b){ } void b: : display{ cout m _ a = 并購(gòu) ,m _ b = m _ bendl}//直接派生類c類c:虛公共a{public: c(int a,int c);public:虛空顯示;protect: int m _ c;};c: :c(int a,int c): a(a),m _ c(c){ } void c: : display{ cout m _ a = 并購(gòu) ,m _ c = m _ c:公共b,公共c{public: d(int a,int b,int c,int d);public:虛空顯示;privat:國(guó)際機(jī)場(chǎng);};de cho 2-@ . com :d(int a,int b,int c,int d): a(a),b(90,b),c(100,c),m _ d(d){ } void de cho 2-@ . com : display{ cout m _ a = 并購(gòu) ,m _ b = m _ b ,m _ c = m _ c ,m _ d = m _ dendl}在最終派生類d的構(gòu)造函數(shù)中,除了b和c的構(gòu)造函數(shù)外,還調(diào)用了a的構(gòu)造函數(shù)。因?yàn)楝F(xiàn)在a在最終的派生類d中只有一份內(nèi)存,如果是b\c構(gòu)造的,編譯器會(huì)很困惑,不會(huì) 我不知道該初始化哪一個(gè)。
虛擬繼承時(shí)間構(gòu)造函數(shù)的執(zhí)行順序與普通繼承不同:在最終派生類的構(gòu)造函數(shù)調(diào)用列表中,不管每個(gè)構(gòu)造函數(shù)出現(xiàn)的順序如何,編譯器總是先調(diào)用虛基類的構(gòu)造函數(shù),然后按照出現(xiàn)的順序調(diào)用其他構(gòu)造函數(shù);對(duì)于普通的繼承,按照構(gòu)造函數(shù)出現(xiàn)的順序調(diào)用它。
對(duì)于普通繼承,基類子對(duì)象總是在派生類對(duì)象的前面(即基類成員變量總是在派生類成員變量的前面),無論繼承級(jí)別有多深,它距派生類對(duì)象頂部的偏移量都是固定的(固定位置)。請(qǐng)看下面的例子:
a{protect: int m _ a1級(jí);int m _ a2};b:公共a{protect: int b1級(jí);int b2};c:公共b{protect: int c1級(jí);int c2};d:公共c{protect: int d1級(jí);int d2};int main{ a obj _ a;b obj _ bc obj _ cd obj _ d返回0;} a類所在的內(nèi)存位置永遠(yuǎn)在前面。
1)修改上面的代碼,使a成為b的虛擬基類:
b:虛擬公共a類
a將移動(dòng)到后面
2)假設(shè)a是b and b的虛基類,是c的虛基類
從上面兩張圖可以看出,虛擬繼承時(shí)的派生類對(duì)象分為兩部分:
沒有陰影的部分有固定的偏移量,不會(huì)隨著繼承級(jí)別的增加而改變,稱為固定部分;陰影部分是虛擬基類的一個(gè)子類,偏移量會(huì)隨著繼承級(jí)別的增加而變化,這部分稱為共享部分。有一個(gè)問題:如何計(jì)算分?jǐn)偛糠值钠屏浚?br>對(duì)于虛擬繼承,派生類分為固定部分和共享部分,共享部分放在最后。幾乎所有的編纂者都在這一點(diǎn)上達(dá)成了共識(shí)。主要區(qū)別在于如何計(jì)算分?jǐn)偛糠值牡窒瑳]有統(tǒng)一的標(biāo)準(zhǔn)。
以下是vs的解決方案示例:
vc引入了虛擬基類表。如果一個(gè)派生類有一個(gè)或多個(gè)虛擬基類,編譯器將在派生類對(duì)象中插入一個(gè)指針,指向虛擬基類表。虛擬基類表實(shí)際上是一個(gè)數(shù)組,數(shù)組中的元素存儲(chǔ)每個(gè)虛擬基類的偏移量字節(jié)。
假設(shè)a是b and b的虛基類,c是c的虛基類,那么每個(gè)對(duì)象的內(nèi)存該模型如下圖所示:
虛擬繼承表存儲(chǔ)了所有虛擬基類(包括直接繼承和間接繼承)相對(duì)于當(dāng)前對(duì)象的偏移量,這樣在通過派生類指針訪問虛擬基類的成員變量時(shí),無論繼承級(jí)別有多深,都只需要一次間接轉(zhuǎn)換。
這種方案還可以避免在有多個(gè)虛擬基類的情況下,使派生類對(duì)象承載太多指針,只需要承載一個(gè)指針。例如,假設(shè)類a、b、c和d的繼承關(guān)系是:
內(nèi)存模型是:
當(dāng)派生類被賦給基類,數(shù)據(jù)類型被轉(zhuǎn)換,int類型的數(shù)據(jù)被賦給float類型的變量時(shí),編譯器會(huì)先把int類型的數(shù)據(jù)轉(zhuǎn)換成float類型再賦值;同樣,類也可以有數(shù)據(jù)類型轉(zhuǎn)換,也是數(shù)據(jù)類型;
但這種轉(zhuǎn)換只在基類和派生類之間有意義,只能將派生類賦給基類,包括將派生類對(duì)象賦給基類對(duì)象,將派生類指針賦給基類指針,將派生類引用賦給基類引用,這在c中稱為向上造型,相應(yīng)地,將基類賦給派生類稱為向下造型。
過渡期間非常安全。
分配的本質(zhì)是將現(xiàn)有數(shù)據(jù)寫入分配的內(nèi)存。對(duì)象的內(nèi)存只包含成員變量,所以對(duì)象之間的賦值就是成員變量的賦值,不存在成員函數(shù)的賦值問題。
這種轉(zhuǎn)換關(guān)系是不可逆的,只能用派生類對(duì)象給基類對(duì)象賦值,不能給派生類對(duì)象賦值。(因?yàn)榛惒话缮惖某蓡T變量,所以不能給派生類的成員變量賦值。同樣,不能在同一基類的不同派生類對(duì)象之間進(jìn)行賦值)。
#使用命名空間std包含iostreamusing//基類class a{public: a(int a);public:虛空顯示;public:國(guó)際機(jī)場(chǎng);};a: :a(int a): m _ a(a){ } void a: : display{ cout a: m _ a級(jí)= m _ a:公共a{public: b(int a,int b);public:虛空顯示;public: int m _ b;};b: :b(int a,int b): a(a),m _ b(b){ } void b: : display{ cout b: m _ a級(jí)= 并購(gòu) ,m _ b = m _ bendl} int main{ a a(10);b b(66,99);//賦值前,a . display;b .顯示器;cout - endl//賦值后,a = b;//此時(shí)a.m_a變成66;a .顯示器;b .顯示器;返回0;}除了將派生類對(duì)象賦給類基類對(duì)象,還可以將派生類指針賦給基類指針:
以下繼承關(guān)系:
#使用命名空間std包含iostreamusing//基類a class a{public: a(int a);public:虛空顯示;protect:國(guó)際機(jī)場(chǎng);};a: :a(int a): m _ a(a){ } void a: : display{ cout a: m _ a級(jí)= m _ aendl}//中間派生類b class b:公共a{public: b(int a,int b);public:虛空顯示;protect: int m _ b;};b: :b(int a,int b): a(a),m _ b(b){ } void b: : display{ cout b: m _ a級(jí)= 并購(gòu) ,m _ b = m _ bendl}//基類c類c{public: c(int c);public:虛空顯示;protect: int m _ c;};c::c(int c): m _ c(c){ } void c:: display{ cout c:級(jí)m _ c = m _ c: public b,public c{public: d(int a,int b,int c,int d);public:虛空顯示;privat:國(guó)際機(jī)場(chǎng);};de cho 2-@ . com :d(int a,int b,int c,int d): b(a,b),c(c),m _ d(d){ } void de cho 2-@ . com : display{ cout d: m _ a級(jí)= 并購(gòu) ,m _ b = m _ b ,m _ c = m _ c ,m _ d = m _ dendl} int main{ a * pa = new a(1);b *pb = new b(2,20);c *pc =新c(3);d *pd =新d(4,40,400,4000);pa = pd//將pd的指針賦給pa;pa 的地址也是pd s地址,pa-display;//但是函數(shù)還是用了pa的函數(shù),和指針類型pb = pd有關(guān);p b- display;pc = pdpc -顯示器;cout - endlcout pa = paendlcout pb = pbendlcout pc = pcendlcout pd = pdendl返回0;可以看出,與對(duì)象變量之間的賦值不同,對(duì)象指針之間的賦值并不復(fù)制對(duì)象的成員,也不修改對(duì)象本身的數(shù)據(jù),只是改變指針的方向。
第一個(gè)問題:為什么pa的地址變了,指向pd?為什么被調(diào)用的函數(shù)仍然屬于pa?
輸出值的解釋:pa本來是指向基類a的指針,現(xiàn)在指向了派生類d的對(duì)象,使得隱式指針發(fā)生了這種變化,也指向了類d的對(duì)象,所以最終在display內(nèi)部使用了類d的成員變量也就不難理解了。
函數(shù)調(diào)用解釋:編譯器雖然通過指針訪問成員變量,但并不通過指針訪問成員函數(shù):編譯器通過指針類型訪問成員函數(shù)。對(duì)于pa來說,它的類型是a,無論指向哪個(gè)對(duì)象,都使用a類的成員函數(shù)..具體原因在前面的注釋中已經(jīng)詳細(xì)解釋過了:c函數(shù)的編譯原理和成員函數(shù)的實(shí)現(xiàn);
第二個(gè)問題:為什么不是 pc的地址和pd的地址不一樣,而pa,pb和pd是一樣的?
我們通常認(rèn)為賦值就是把一個(gè)變量的值賦予另一個(gè)變量。雖然這種想法是正確的,但是需要注意的是,編譯器可能會(huì)在賦值之前處理現(xiàn)有的值。比如將double類型的值賦給int類型的變量,編譯器會(huì)直接擦除小數(shù)部分,導(dǎo)致賦值運(yùn)算符兩邊的變量值不相等。
將派生類的指針賦給基類的指針時(shí)也是如此,編譯器也可能在賦值前對(duì)其進(jìn)行處理:
對(duì)象的指針必須指向?qū)ο蟮钠鹗嘉恢?。?duì)于a類和b類,其子對(duì)象的起始地址與d類相同,所以在給pa和pb賦值pd時(shí)不需要做任何調(diào)整,直接傳遞已有的值即可;但是,c類子對(duì)象與d類對(duì)象的開頭有一定的偏移。在給pc賦值pd時(shí)要加上這個(gè)偏移量,這樣pc就可以指向c類子對(duì)象的起始位置,即執(zhí)行pc = pd語句,編譯器調(diào)整了pd的值,導(dǎo)致pc和pd的值不同。
內(nèi)存模型:
將派生類引用分配給基類引用
引用只是封裝了指針,本質(zhì)上沒有區(qū)別,所以我猜測(cè)把派生類引用賦給基類引用的效果應(yīng)該和指針一樣。
//更改上面main函數(shù)中的content int main {d d d (4,40,400,4000);a ra = d;b rb = d;c rc = d;ra . display;rb.display。rc . display;返回0;}果不其然,運(yùn)行結(jié)果是:a:類m _ a = 4 b:類m _ a = 4,m _ b = 40 cecho2-@.co類。m m c = 400
具體分析同指針;
你可以去看看這個(gè)博客來增強(qiáng)對(duì)向上轉(zhuǎn)化的理解-。
標(biāo)簽:
函數(shù)派生類
了解更多c引用類型(c引用的定義)相關(guān)內(nèi)容請(qǐng)關(guān)注本站點(diǎn)。
上一個(gè):個(gè)人繳納社保怎么交
下一個(gè):供應(yīng)1000g 2000g 3000g壓力數(shù)顯式外置電子測(cè)力計(jì)

我的榮耀手游官網(wǎng)下載(我的榮耀 游戲)
德國(guó)HOLOEYE空間光調(diào)制器PLUTO-2
你的小青柑是發(fā)了霉還是長(zhǎng)了白霜?
仙客來如何育苗
三方協(xié)議可以蓋店鋪嗎?
七險(xiǎn)二金包括什么
陰陽師網(wǎng)易版進(jìn)不了冬之雪(陰陽師冬日)
word中打空格鍵就刪除是什么原因(word里面打空格鍵直接刪除字了)
巨峰葡萄的養(yǎng)殖方法和注意事項(xiàng)
導(dǎo)入kennametal產(chǎn)品(從根源上提升機(jī)械紡織生產(chǎn)的質(zhì)量和效率)
十八禁 网站在线观看免费视频_2020av天堂网_一 级 黄 色 片免费网站_绝顶高潮合集Videos