[英]virtual function and derived class
讓我首先向您展示簡單的C ++代碼示例。
#include <iostream>
using namespace std;
class Person{
string m_name;
public:
virtual void saySomething(){
cout << "I am a person.";
}
};
class Student:public Person{
public:
void saySomething(){
cout << "I am a student." << endl;
}
};
class CSStudent:public Student{
public:
void saySomething(){
cout << "I am a CS Student." << endl;
}
};
int main(){
Person* p = new Person();
Student* s = new Student();
CSStudent* c = new CSStudent();
((Student*)c)->saySomething();
return 0;
}
在我的示例中,Student是Person的派生類。 另外,CSStudent是Student的派生類。 我知道虛擬關鍵字可以確定它是否是派生類。
問題1
((學生*)c)-> saySomething();
為什么會收到“我是一名CS學生”? 我期望“我是學生”。 因為我只為Person指定了虛擬關鍵字。
問題2。
我看到了一個將虛擬關鍵字僅置於基本情況下的示例。 另外,我看到了一個將虛擬關鍵字應用於所有基本案例和派生類的示例。 兩者有什么區別?
問題3。
虛擬功能是在運行時確定的。 非虛擬功能呢? 是在編譯時確定的嗎?
對於第一個問題,即使將c
CSStudent
基於同一基類的另一個類,它仍然是原始類(即CSStudent
)。 虛函數(幾乎總是)是通過跳轉表實現的。 因此, c
對象具有一個表,其中包含c
對象中實際函數的地址。 該表不會更改,因為您重新輸入了類型。
對於第二個問題,沒有區別。 如果在基類中將成員函數標記為virtual
,則在所有子類中也將其虛擬。 只是有些人也喜歡將子類中的功能標記為virtual
。 它實際上可能很好,因為這樣您就不必檢查基類即可查看哪些函數是虛擬的,哪些不是。
第三點。 是的,虛擬函數是使用如上所述的表在運行時計算的,而非虛擬函數是在編譯時確定的。
問題1
((學生*)c)-> saySomething();
為什么會收到“我是一名CS學生”? 我期望“我是學生”。 因為我只為Person指定了虛擬關鍵字。
在C ++中,如果基類指定一個函數為virtual
,則對於具有相同簽名的成員函數,您不需要任何派生類中的virtual
關鍵字。 您可以根據需要添加它(通常這樣做是一種很好的做法),但這是可選的。
問題2。
我在網頁上看到了一些示例,其中僅將虛擬關鍵字提到了基本情況。 另外,我看到了一些在所有基本案例和派生類中提及虛擬關鍵字的示例。 兩者有什么區別?
如上所述:沒有區別。 如果基類說具有特定簽名的方法是虛擬的,則該方法也適用於所有派生類。
問題3。
虛擬功能是在運行時確定的。 非虛擬功能呢? 是在編譯時確定的嗎?
是。 如果您的saySomething()
函數不是虛擬的,則調用將在編譯時解決,並且將其saySomething()
為Student*
意味着您將獲得Student
版本的saySomething()
。
為什么會收到“我是一名CS學生”? 我期望“我是學生”。 因為我只為Person指定了虛擬關鍵字。
在C ++中,如果在基類中指定了virtual關鍵字,則該方法也將自動在任何子類中變為虛擬。 (我不會試圖推測為什么C ++可以那樣工作,但是確實可以)
我看到了一個將虛擬關鍵字僅置於基本情況下的示例。 另外,我看到了一個將虛擬關鍵字應用於所有基本案例和派生類的示例。 兩者有什么區別?
在功能上沒有區別。 從風格上講,我認為后者在自我記錄方面要好一些。
虛擬功能是在運行時確定的。 非虛擬功能呢? 是在編譯時確定的嗎?
正確。 特別是,將為非虛擬方法調用的方法實現將完全由調用該方法的指針類型確定,而不是通過對適當子類方法的動態查找來確定。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.