2012-06-27 49 views
1

所以我知道,從各種在線資源,這通常是一個沒有沒有從構造函數中調用虛函數。我意識到這裏的問題是基類將首先構造,而C++將首先調用基類的函數版本。不過,我有一個獨特的用例,可能與此無關。我會欣賞一些評論。考慮這種情況。虛函數調用

class Base 
{ 
public: 
    Base(string data) 
    { 
     Parse(data); 
    } 
    ~Base(){} 
private: 
    virtual Parse(string data); 
} 

class Derived : public Base 
{ 
public: 
    Derived(string data) 
    { 
     Parse(data); 
    } 
    ~Derived(); 
private: 
    Parse(string data); 
} 

比方說,我有這樣的設置和我的預期每一個派生類的行爲是:

  1. 解析被調用基類來解析出什麼應該是共同所有這些輸入字符串。
  2. 派生的分析應該獲取特定於派生類的數據。

是否有意義在這種情況下使用虛擬函數的構造?或者我每次構建這個課程時都不得不「公開解析」並稱之爲「解析」?或者還有其他建議。

我希望這是有道理的,並請原諒以上的任何語法錯誤,我只是想表達一個總體思路。

+0

沒關係。行爲完全由標準定義,依賴於這不是一個「不」。只有當人們天真地期望有不同的事情發生時,這是一個不容否認的事情。不過,如果你的同事稍後再來,並且被你的代碼弄糊塗,那麼這也可能是一個不好的做法。我更關心的是'Parse'是否實際上是由'Base'中的其他函數調用的。如果是這樣,那爲什麼派生的'Parse'只解析特定於派生類的數據並不重要?如果沒有,那麼將它作爲私有虛擬功能有什麼意義呢? –

+2

如果它按照您的描述工作,爲什麼Parse虛擬? –

+0

您仍然可以在派生類中創建'Parse()'虛擬,但是可以從構造函數Derived :: Parse(data)'中顯式地調用它。 –

回答

0

的解決方案很簡單:

class Base 
{ 
public: 
    Base(string data) 
    { 
     Parse(data); 
    } 
    ~Base(){} 
private: 
    void Parse(string data); 
} 

class Derived : public Base 
{ 
public: 
    Derived(string data) 
    { 
     ParseMore(data); 
    } 
    ~Derived(); 
private: 
    void ParseMore(string data); 
} 

Derived構造的Base調用構造函數輸入的Derived構造之前。 因此,Base中發生的解析將完成,並且您可以在Derived構造函數中完成解析。

+0

是的,這基本上是它應該的。雖然我很確定這兩個函數都可以稱爲Parse對嗎?我不認爲自私以來有什麼不妥。但是,在這裏使用虛擬功能並不是正確的想法。 – anoneironaut

0

不要從構造函數調用虛函數。你將不會得到多態行爲,因爲將使用基類虛擬表。

如果您不需要多態行爲 - 不要讓功能虛擬

+0

提問者不需要多態行爲。 X不給多態行爲。那麼X的問題是什麼? –

+0

@SteveJessop:我認爲最好在這種情況下使這個函數非虛擬 – Andrew

+0

我可以想象一下,你只想在構造函數中使用'Parse'的非虛擬行爲,以及其他地方的虛擬行爲。然而,這樣做有點乏味,所以我會讓提問者回答上述評論中提出的問題,而不是提供一個可能不適用於此案例的理由。我猜想,即使它有道理,人們也可以爭辯說,這些類應該有一個在構造函數中使用的非虛函數,並且也在每個類的虛函數的實現中調用。避免混淆。 –

1

還是我被迫做出「解析」市民和調用它每一次我構建這個類?

其實,在這種情況下,因爲要避免多態行爲,我不明白究竟爲什麼你必須做出Parse一個虛擬的方法,甚至類的非靜態方法,因爲它不修改類本身......舉例來說的任何數據成員,你可以有Parse作爲私人static方法,然後簡單地調用ClassType::Parse()中的每個對象的構造函數,你會得到相同的功能。

1

但絕對沒有錯,使用虛函數的構造,只要你的作品。重要的是要記住,從構造函數調用時,虛函數的多態行爲總是侷限於整個層次結構的已構建子集。 (類似的規則適用於析構函數)。

如果該限制的虛擬行爲適合您的目的,他們通過各種手段使用它。

您必須參考的「不否」參數是一個衆所周知的假參數,它基於用戶期望函數調用[尚未構造]派生類的人爲前提。爲什麼有些人將這種虛假的前提轉化爲不應該從施工人員那裏調用虛擬功能的結論。我還沒有看到可靠的解釋。

0

這個的最簡單的解決方案是使用策略模式:定義一個 抽象基類Parser,具有純虛函數parse,和 已經派生類將指針傳遞給他們的解析器 的實例,以基類構造函數;即:

class Base 
{ 
protected: 
    class Parser 
    { 
    public: 
     virtual ~Parser() {} // Probably not necessary, since no 
           // one is going to dynamically 
           // allocate any of these, but better 
           // safe than sorry. 
     virtual void parse(std::string const& data) const = 0; 
    }; 

    Base(Parser const& derivedClassParser, std::string const& data) 
    { 
     derivedClassParser.parse(data); 
    } 
public: 
    // ... 
}; 

每個派生類將限定其解析器,從 Base::Parser衍生,定義它的一個靜態實例,並傳遞的 此靜態實例的地址向下的基類。

還有另一種可能性;它不一定正常工作,如果 你有對象的臨時實例,但它可以是有用的,如果因爲 某些原因你不能使用上述模式。基本上,你定義了一個特殊的類,它在析構函數中調用虛函數,而 有一個隱含的從std::string(也可能從char const*轉換來支持傳遞字符串文字)的轉換,並聲明你的 構造函數這個班;例如:

class Base 
{ 
public: 
    class CallVirtual 
    { 
     std::string myData; 
     mutable Base* myOwner; 
     friend class Base; 
    public: 
     CallVirtual(std::string const& data) 
      : myData(data) 
      , myOwner(NULL) 
     { 
     } 
     ~CallVirtual() 
     { 
      if (myOwner != NULL) { 
       myOwner->Parse(myData); 
      } 
     } 
    }; 

    Base(CallVirtual const& dataArg) 
    { 
     dataArg.myOwner = this; 
     // ... 
    } 

    virtual void Parse(std::string const& data) ... 
}; 

派生類還應該採用CallVirtual const&作爲參數。 然後,在創建派生類的一個實例:

Base* p = new Derived(someString); 

,字符串被自動轉換爲一個臨時CallVirtual, 其析構函數將在充分表達的端部被調用。