訪問派生類的成員有可見性和唯一性兩個問題,
我們只能訪問到具有唯一性的可見成員。
先說說可見性的問題。如果有兩個以上具有包含關係的作用域,
外層作用域中的識別符號
在內層作用域中沒有同名識別符號則它在內層作用域中也是可見的,但是如果在內層作用域中存在同名識別符號則外層的識別符號被遮蔽,這也叫做同名覆蓋
。派生類在
繼承時,基類的成員和派生類的新增成員都有類作用域,但是是兩個具有包含關係的作用域,派生類作用域位於內層。
如果在派生類中存在乙個和基類某資料成員同名的資料成員,或者和基類某成員函式的名稱和參數列都相同的成員函式,則派生類中的新成員就覆蓋了基類成員,不管是在派生類內還是在派生類外部只能通過成員名訪問到派生類的成員,而訪問不到基類成員。
如果需要在派生類中訪問基類中的同名成員怎麼辦呢?
我們可以通過基類名和作用域分辨符來訪問基類中的同名成員。作用域分辨符就是「::」,
在派生類內部
訪問基類同名成員的語法形式是:
基類名::資料成員名; // 資料成員
基類名::函式成員名(參數列); // 函式成員
如果是在派生類外
通過派生類物件訪問的話,前面還要加上「派生類物件名.」:
派生類物件名.基類名::資料成員名; // 資料成員
派生類物件名.基類名::函式成員名(參數列); // 函式成員
這裡的基類名就限定了後面的成員屬於哪個類。
如果是多繼承的話,要考慮派生類的基類有沒有共同基類的問題。先說說多個基類之間沒有繼承關係也沒有共同基類的情況。這種情況下如果多個基類具有同名成員,派生類也新增了同名成員,則派生類成員會覆蓋所有基類中的同名成員。通過成員名只能訪問到派生類的成員,要訪問各基類的同名成員就需要使用作用域分辨符。而如果派生類中不存在同名成員,訪問多個基類的同名成員也需要使用作用域分辨符,因為從不同基類繼承過來的同名成員具有相同的作用域,通過成員名無法唯一標識成員,所以也需要作用域分辨符來分辨。
給大家乙個多繼承情況下同名覆蓋的例子:
派生類child由基類base1和base2公有繼承
而來,兩個基類有同名資料成員x和同名函式
成員show,派生類child又新增了同名的資料成員x和同名的函式成員show,這樣派生類child中就一共有6個成員,三個同名的資料成員和三個同名的函式成員。
#include
using namespace std;
class base1 // 基類base1的宣告
;class base2 // 基類base2的宣告
;class child : public base1, public base2 // 派生類child的宣告
;int main()
程式執行結果:
x of child: 5
x of base1: 7
x of base2: 8
主函式main中宣告了派生類child的物件child,因為同名覆蓋,所以通過成員名只能訪問派生類child的成員,要訪問基類base1和base2的同名成員就需要像上面那樣使用作用域分辨符訪問。
如果在派生類child的成員函式
show中訪問基類base1的同名成員,比如x,則可以將child的show函式修改為:void show() ;
程式其餘部分不變,則主函式main中的語句child.x = 5;和child.show();就會編譯報錯,
因為這兩個識別符號
具有二義性,系統無法唯一標識它們,不知道該訪問哪個成員
。只能通過作用域分辨符來訪問。
上面對於多繼承的討論都是假設多個基類之間沒有繼承關係也沒有共同基類的情況,而如果派生類的全部或者部分基類有共同的基類,也就是說派生類的這些基類是從同乙個基類派生出的,那麼派生類的這些直接基類從上一級基類繼承的成員都具有相同的名稱,即都是同名成員,要訪問它們就必須通過直接基類限定,使用作用域分辨符訪問。
上面說的可能有些抽象,再給出個程式例子來說明這種情況吧。我們先宣告乙個基類base0,base0中有資料成員x和函式
成員show,
再宣告類base1和base2,它們都由base0公有繼承而來,
最後從base1和base2共同派生出類child
。這時base0的成員經過到base1和base2再到child的兩次派生過程,出現在child類中時,
實際上base0的資料成員x已經是兩個不同的成員,只是名稱相同但是在記憶體中是兩份,函式成員show也是兩個不同的成員,只是名稱相同但是函式體可能不同。
這就需要使用作用域分辨符訪問了,但是不能用基類base0來限定,因為這樣還是不能說明成員是從base1還是base2繼承而來,所以必須使用直接基類base1或者base2來限定,達到唯一標識成員的目的。程式例子如下:
#include
using namespace std;
class base0 // 基類base0的宣告
;class base1 : public base0 // 由base0派生的類base1的宣告
;class base2 : public base0 // 由base0派生的類base2的宣告
;class child : public base1, public base2
;int main()
程式執行結果:
x of base0: 3
x of base0: 5
上面的主函式
main中定義了派生類child的物件child,
如果只通過成員名訪問成員x和show,系統就不能確定訪問哪個x和哪個show,這就需要使用直接基類base1或者base2和作用域分辨符來訪問它們。
上面說了,資料成員x在記憶體中有兩份拷貝,可以存放不同的數值,但是一般我們只需要乙個這樣的拷貝,那多出來的那個就是對記憶體的浪費。解決這個問題就需要虛基類技術。
四十一 繼承與派生 作用域分辨符
作用域分辨符主要是為了處理基類和派生類成員的可見性問題。派生類在繼承時,基類成員和派生類成員都有類作用域。兩個具有包含關係的作用域,派生類位於內層。當派生類和基類中存在同名的資料成員或函式成員時,派生類的新成員覆蓋了基類成員。也就是說在派生類內還是在派生類外部都只能訪問到派生類成員,同名基類成員無法...
派生與繼承 多重派生
1 理解下面的程式,並在 vc 6.0 下執行檢視結果,回答程式後面的問題。class cbase1 cbase1 void print protected int a class cbase2 cbase2 void print protected int b class cderive publ...
繼承與派生
實驗內容 建立乙個基類,兩個派生類,讓每乙個派生類都包含乙個函式area 分別用來返回矩形和三角形的面積。用建構函式對height和width進行初始化。要求通過基類指標訪問虛函式的方法。純虛函式 virtual double area 0 純虛函式的作用是在基類中為派生類保留乙個函式的名字,以便派...