C++ Primer 第十五章 面向對象程序設計 15.6 繼承中的類作用域 練習和總結

15.2 繼承中的類作用域

每一個類都有自己的作用域,派生類的作用域在基類的作用域中。從派生類的對象訪問某一個成員如果在派生類中沒有找到該名字,則從直接基類中查找,一直往繼承鏈的上面找,如果一直沒有找到則報錯。

對於名字查找,這依賴於變量(普通變量,指針或者引用)的靜態類型,即:變量的靜態類型決定了這個變量只能訪問哪些成員這就很好理解爲什麼用基類的引用或者指針只能訪問基類的成員(虛函數除外),另一個理解的角度是基類的引用或者指針實際上綁定的是派生類中基類的對象。

如果基類和派生類的成員的名字有重複的,則派生類的成員會隱藏基類的成員。這和之前作用域中進行名字查找其實是一樣的。如果想訪問被隱藏的成員則使用作用域運算符::來訪問

繼承體系中名字查找的順序是下面這樣的。

在這裏插入圖片描述
1.首先由靜態類型確定可以訪問的成員
2.從派生中的作用域中開始查找,如果沒有找到則從直接基類中查找名字
3.如果還沒有找到則,再從該類的直接基類中找,重複這個步驟,直到找到或者沒有找到位置
4.如果沒有找到則報錯
5.如果找到了,且這個名字是一個虛函數且我們通過指針或者引用調用,則運行的時候根據動態類型來調用對應的函數,如果這個名字不是虛函數或者我們是使用普通的方式(非指針和引用)來訪問的,則在編譯時產生一個常規函數調用。

第5點尤其重要,分清楚什麼時候運行時確定調用,什麼時候編譯時確定調用的函數。

之前說了同名的派生類成員會覆蓋基類的成員,對於函數來說只要名字是一樣的基類的同名函數就會被隱藏。所以在重寫虛函數的時候,如果形參列表不一樣,也會覆蓋基類的函數,但是派生類其實是定義了一個新的虛函數(如果加了virtual關鍵字的話),所以override的重要性體現出來了。

如果基類有函數是重載的,派生類可以重寫這些函數,但是因爲只要名字相同基類的同名函數就會被隱藏,所以如果想要基類所有的重載版本對於派生類來說都是可見的,那麼我們只能全部重寫或者一個都不重寫。

但是可能我們對於多個重載函數,只有少數幾個需要修改一下,其餘的可以全部照抄。這個時候我們可以使用using關鍵字,我們只要寫using 基類名::函數名;即可,這就表示基類所有的同名函數都添加到派生類的作用域中,接下來直接寫我們想要修改的函數即可。

struct A
{
	void func() {
		cout<<"A-->func()"<<endl;
	};
	void func(int) {
		cout << "A-->func(int)" << endl;
	};
	void func(double, double) {
		cout << "A-->func(double, double)" << endl;
	};
};
struct B:public A
{
	using A::func;
	void func(double, double) {
		cout << "B-->func(double, double)" << endl;
	}
};

---
B b;
b.func();
b.func(1);
b.func(0.0,0.0);

A-->func()
A-->func(int)
B-->func(double, double)

練習

15.23

將D1中int fcn(int);變爲virtual int func() override;
bp2->func()調用的是D1自己的版本

注意下圖這種方式,D2的fcn()是一個虛函數,雖然D1中的fcn(int)隱藏了fcn(),但是後續還是可以重寫虛函數。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章