2017-07-27 72 views
8

我正在學習C++並且正在學習有關virtual關鍵字的知識。我搜索了互聯網試圖瞭解它無濟於事。我進入了我的編輯器,做了下面的實驗,期待它打印兩次基本消息(因爲我的印象是需要virtual關鍵字來覆蓋函數)。但是,它打印出兩個不同的信息。有人可以向我解釋爲什麼我們需要虛擬關鍵字,如果我們可以簡單地覆蓋功能,並仍然看起來像多態行爲?也許有人可以幫助我和其他人在未來理解虛擬與壓倒一切。 (我得到的輸出是「我是基礎」,後面是「我是派生的」)。C++虛擬關鍵字vs覆蓋函數

#include <iostream> 

using namespace std; 
class Base{ 
public: 
    void printMe(){ 
     cout << "I am the base" << endl; 
    } 
}; 
class Derived: public Base{ 
public: 
    void printMe(){ 
     cout << "I am the derived" << endl; 
    } 
}; 
int main() { 
    Base a; 
    Derived b; 
    a.printMe(); 
    b.printMe(); 
    return 0; 
} 
+2

注意:'使用命名空間標準;'是一個不好的習慣,如果你現在可以停下來,你可能會避免將來的很多麻煩。 'std ::'前綴是有原因的:它避免了與你自己的類,結構和變量的衝突。 – tadman

+3

試試'Base * p = new Derived; p-> printMe();'有和沒有'虛擬'。 – HolyBlackCat

+7

澄清 - 當通過指針或對其基類的引用訪問對象時,實現多態行爲。 – DeiDei

回答

11

請考慮以下示例。說明需要virtualoverride的重要線是c->printMe();。請注意,c的類型是Base*,但由於多態性,它能夠正確地從派生類中調用重寫的方法。

#include <iostream> 

class Base{ 
public: 
    virtual void printMe(){ 
     std::cout << "I am the base" << std::endl; 
    } 
}; 

class Derived: public Base{ 
public: 
    void printMe() override { 
     std::cout << "I am the derived" << std::endl; 
    } 
}; 

int main() { 
    Base a; 
    Derived b; 
    a.printMe(); 
    b.printMe(); 
    Base* c = &b; 
    c->printMe(); 
    return 0; 
} 

輸出是

I am the base 
I am the derived 
I am the derived 
+3

雖然,你還沒有解釋'override'關鍵字的用途是什麼。 –

+0

我做過(只有我),但人們傾向於首先回答他們看到的投票。 –

2

你不在這裏看到的行爲,因爲你已經宣佈bDerived所以編譯器知道哪些功能使用的類型。爲了揭露爲什麼virtual是必要的,你需要的東西混合起來:

int main() { 
    Base a; 
    Base *b = new Derived(); 

    a.printMe(); 
    b->printMe(); 

    delete b; 

    return 0; 
} 

現在bBase*型這意味着它要在虛函數表上使用Base加任何的功能的。這打破了你的實現。你可以通過正確地聲明virtual來修復它。

+0

你能解釋你對第3行新關鍵字的使用嗎?我對這方面的新作品有點生疏。我知道你正在創建一個基類型的指針;那麼你是否給它分配足夠的內存來保存派生類?第3行如何工作? –

+0

這只是一個使用'new'的直接C++分配。這很像C的'malloc',但內置更多的智能,並且自動調用初始化器。一本好的C++參考書應該涵蓋'new'和'delete'的基礎知識。如果你沒有,那麼[C++的作者](http://www.stroustrup.com/4th.html)是一個很好的開始。 – tadman

+1

@tadman:當我評論其他答案時,我認爲我們最好不要在示例代碼中爲新手使用裸'new'和'delete',因爲它們在現代C++中通常被認爲是不好的練習。參考文獻(或'b =&a')也可以說明這一點。 –

6

隨着你的代碼,如果你這樣做

Derived derived; 
Base* base_ptr = &derived; 
base_ptr->printMe(); 

你覺得會發生什麼事?它不會打印出I am the derived,因爲該方法不是虛擬的,並且調度是通過調用對象的靜態類型完成的(即Base)。如果將其更改爲虛擬方法,則調用的方法將取決於對象的動態類型而不是靜態類型。

2

override是加入C++ 11一個新的關鍵字。

你應該使用它,因爲:

  • 編譯器會檢查是否有基類包含一個匹配virtual方法。這很重要,因爲方法名稱或參數列表中的某些拼寫錯誤(允許使用重載)可能會導致出現這樣的情況,即當它沒有被重寫時會被覆蓋。

  • 如果對一種方法使用override,如果在不使用關鍵字override的情況下重寫另一種方法,則編譯器將報告錯誤。這有助於在符號衝突發生時檢測到不需要的覆蓋。

  • virtual並不意味着「覆蓋」。在類doent使用「覆蓋」關鍵字比重寫一個方法,你可以簡單地寫這個方法省略「虛擬」關鍵字,重寫將隱式發生。開發人員在C++ 11之前編寫了virtual,以表明他們的覆蓋意圖。簡單地說就是virtual的意思是:這個方法可以在一個子類中重寫。

+0

「虛擬並不意味着」覆蓋「,如果你忽略它,覆蓋仍然有效。」?我認爲你的意思是,如果你在派生類中忽略它,而不是在基數中省略 – ROX

+0

是的,這是我的意思。我改進了文字,使其更加清晰。 –