2012-01-03 58 views
1

從Java的角度來看,我驚訝地發現只能覆蓋具有virtual關鍵字的基本方法。 在Java中,您使用final關鍵字來聲明方法不能被覆蓋。C++中的重寫規則

我曾在我的腦海的想法,你很少想禁止壓倒一切,從而使他人可以擴展你的類他們如何認爲合適的。

在C++

所以,如果你覺得有人會從你的類中的一些階段繼承想(也許幾年後有人認爲它是一個很酷的想法),你讓所有的方法虛擬?

還是有希望爲C++中,我不知道禁止一些這方面的關鍵原因是什麼?

借鑑這是試驗我每種語言做的:

的Java

public class Base { 

    void doSomething(){ 
    System.out.println("Doing the base thing"); 
    } 
    } 
    public class Derived extends Base { 

    void doSomething(){ 
    System.out.println("Doing the derived thing"); 
    } 

    public static void main(String... argv){ 
     Base object = new Derived(); 
     object.doSomething(); 
    } 
    } 

C++

class Base 
    { 
    public: 
     Base(void); 
     ~Base(void); 
     virtual void doSomething(); 
    }; 

    void Base::doSomething(){ 
     std::cout << "I'm doing the base thing\n" << std::endl; 
    } 

    class Derived : 
     public Base 
    { 
    public: 
     Derived(void); 
     ~Derived(void); 
     void doSomething(); 
    }; 

    void Derived::doSomething(){ 
     std::cout << "I'm doing the dervied thing" << std::endl; 
    } 


    int main(void){ 

     Base * object = new Derived; 

     object->doSomething(); 
     return 0; 

    } 
+0

的可能重複的[當在C++標記的功能作爲虛擬?](http://stackoverflow.com/questions/8298041/when-to-mark-a-function-in-c-as-a-虛擬) – 2012-01-03 10:35:59

回答

1

duffymo和Als正朝着正確的方向引導你。我只是想在一件事情上,你說要評論:

所以在C++中,如果你覺得有人可能要在某些階段從你的類繼承 (也許幾年後有人認爲它是一個很酷的想法)做 你讓你的所有方法變得虛擬?

從軟件工程的角度來看:如果你沒有立即使用繼承並且沒有使用接口,那麼我不建議你聲明你的方法是虛擬的。

虛擬方法拿出一個曾經如此輕微性能下降。對於非關鍵代碼路徑,性能影響可能可以忽略不計。但是對於被調用很多的類方法,它可以加起來。編譯器不能做太多內聯和直接鏈接。相反,被調用的虛擬方法必須在運行時在v表數組中查找。

當有人對我的編碼團隊開始了與設計談話時,我的「未來驗證」反模式警報響起「有人可能想在某個時候後...」那是。設計可擴展性是一回事,但「未來的特徵」應該推遲到那個時候。

再說 - 這傢伙年以後誰認爲這是一個很酷的想法 - 讓他自己轉換類方法是虛擬的一個。無論如何,你將會進入更大的項目。 :)

+0

注意的虛方法對性能的影響僅僅是對C真正++或者有實際上不同的覆蓋方式(甚至那麼可以樂觀地內聯等) – Voo 2012-01-03 21:18:05

1

是,在C++中一個類的方法只能是overidden,如果它在基類中標記爲virtual

如果你的類是繼承做你的類方法是指爲基地提供不同的行爲和導出然後標記方法virtual

良好閱讀:

When to mark a function in C++ as a virtual?

1

是的,你必須讓所有的方法是虛擬的。

的Java採取的立場是一切都是公平的遊戲默認覆蓋,並禁止其所需的操作。 C++和C#採取相反的觀點。

0

您可以覆蓋在基類的方法,即使沒有virtual

就拿這個小程序,例如:

#include <iostream> 

struct A 
{ 
    void m() 
     { std::cout << "A\n"; } 

    virtual void n() 
     { std::cout << "A\n"; } 
}; 

struct B : public A 
{ 
    void m() 
     { std::cout << "B\n"; } 

    virtual void n() 
     { std::cout << "B\n"; } 
}; 

int main() 
{ 
    A a; 
    a.m(); 
    a.n(); 

    B b; 
    b.m(); 
    b.n(); 

    A &c = b; 
    c.m(); 
    c.n(); 
} 

從程序的輸出是:

 
A 
A 
B 
B 
A 
B 

正如你所看到的,B::m覆蓋在A同樣的方法方法。但是,這隻適用於使用B的確切實例。在第三種情況下,如果使用對基類的引用,則需要virtual才能使其工作。

+4

'虛擬'關鍵字對於'overidding'是必需的,或者你得到的是'函數隱藏'。在派生結構'B'中的方法'm()'隱藏了Base結構'A'方法,而'B'中的方法'n()'覆蓋了Base結構方法。 – 2012-01-03 10:41:10

+0

你在B中對m()所做的操作稱爲重載(這也會導致基類版本被隱藏),而不是重寫。在C++中,函數必須被明確聲明爲虛擬的才能覆蓋它。 – 2012-01-03 21:02:34

0

程序員從那裏虛擬函數是默認語言來趨​​向於驚訝的是C++有默認值的反向選擇,即非虛擬是默認值。但是請注意,一個[具體]虛函數的合同是一個困難得多文檔和測試,因爲它實際上涉及到兩個不同的合同:

  1. 的所有重載函數的契約,即什麼樣的功能概念上做
  2. 的具體功能恰好做

僅文檔化的,這些人會不會真正減少,因爲另一半是不明確的,在所有的什麼合同。這也意味着你不能從特定的實現中瀏覽實際的合同(假設你處於非常不典型的情況下,文檔由源提供)。

+1

爲什麼把一切都虛擬或一般繼承的一個例子可能不真的是一個好主意,經常看看所有那些從Java中'HashMap'繼承而來的人並不像他們認爲的那樣。 – Voo 2012-01-03 21:19:14