2010-07-21 35 views
7

我正在閱讀Google C++風格指南,並在Doing Work in Constructors部分中感到困惑。一個在構造函數中做重活的缺點是:懷疑在構造函數中工作的一個缺點

如果工作調用虛函數, 這些調用將不會分派到 子類實現。未來 修改您的班級可以悄悄地 介紹這個問題,即使您的 類目前沒有子類, 造成很多混淆。

我不明白這是什麼意思。有人可以提供解釋,爲什麼這可能被認爲是一個劣勢?

+2

這就是我不喜歡谷歌C++風格指南的原因之一。作爲一個經驗法則,應該避免從構造函數和析構函數調用虛擬方法 - 避免只有在有充分理由並且能夠在代碼審查中保護它的情況下才能做到這一點。另一方面,使用兩步初始化的建議(可以說)是錯誤的,許多其他樣式指南建議不要使用兩步初始化... – 2010-07-21 21:44:56

+2

請記住,Google的樣式指南不適用於C++,儘管頁面標題。他們禁止例外,給出一種混雜的語言,其中許多C++習語不能被使用,或被剝奪了其用處。這就是他們需要兩階段初始化和所有容易出錯的麻煩的原因;沒有辦法指出構造函數的失敗。除非您有特定的理由要遵循Google的風格,否則請忽略它。 – 2010-07-21 22:49:15

回答

9

我大搖大擺地從維基百科頁面Virtual function剝去一些示例代碼:

#include <iostream> 
#include <vector> 

class Animal { 
    public: 
     virtual void eat() const { 
      std::cout << "I eat like a generic Animal." << std::endl; 
     } 
     virtual ~Animal() { 
     } 
}; 

class Wolf : public Animal { 
    public: 
     void eat() const { 
      std::cout << "I eat like a wolf!" << std::endl; 
     } 
}; 

class Fish : public Animal { 
    public: 
     void eat() const { 
      std::cout << "I eat like a fish!" << std::endl; 
     } 
}; 

如果調用eat()Animal構造函數中,它會調用Animaleat()功能每次。即使創建了WolfFish對象,由於Animal構造函數將在子類對象初始化之前完成,所以覆蓋的eat函數將不存在。

這是一個缺點,因爲它可能會導致預期和實際發生之間的混淆。如果我覆蓋eat然後創建我的子類的對象,我期望我的覆蓋函數被調用,即使從Animal引用。我期待它,因爲這是通過構造函數之外的代碼顯式調用時發生的情況。構造函數內部的行爲不同,導致我在困惑中撓撓頭。

+8

沒有'class Lizard'? – 2010-07-21 20:31:33

+2

@Justin:我不能*相信*我錯過了這個機會。 ;) – 2010-07-21 20:33:38

4

當一個對象被構造時,基類的構造函數被首先調用。由於派生類尚未初始化,因此在基類構造函數期間對虛方法的任何調用都不會有派生類對象進行工作。

0

構造函數的工作僅僅是初始化對象的初始狀態。如果您開始執行依賴於虛函數中所採取操作的任務,那麼當子類開始實現這些功能時,您可能會得到一些意想不到的結果。

1

如果您從該類繼承,則在此情況下,您將覆蓋/實現的方法將不會被調用。所以,如果Employee在構造函數中調用work(),那麼稍後您會提出Hourly :: work()和SalariedEmployee :: work(),那些將不會被調用。即使他們有不同的實現,他們仍然被視爲員工,而不是他們的特殊實現。

2

當創建一個子類的實例時,首先被稱爲基類的構造函數,然後是子類的構造函數。

如果基類構造函數調用虛方法,則將調用基類的方法而不是子類,但實例是子類的實例。這可能是一個問題。

多很多信息在這裏:http://www.artima.com/cppsource/nevercall.html

0

虛擬函數調用的行爲不是虛擬的,但是非常令人驚訝,因此編譯器通常會發出警告。