2011-10-26 19 views
3

所以我有這種設置實施前調用虛函數

class Base 
{ 
public: 
    Base(); 
    virtual void parse() = 0; 
}; 

Base::Base() 
{ 
    parse(); 
} 

class Sub : public Base 
{ 
public: 
    Sub(); 
    void parse(); 
}; 

Sub::Sub() : Base() {} 

void Sub::parse() 
{ 
    // DO stuff 
} 

我不知道是否有安韋我可以做一些類似的,現在我得到那個說我可以到一個錯誤不要稱爲純粹的虛擬功能,這是有道理的。有沒有關鍵字可以用來完成這項工作?

我認爲使parse()只是虛擬的,而不是純粹的虛擬會起作用,但我希望用戶必須覆蓋它。

回答

10

在構造函數(或析構函數)根本不會讓你在一個重寫的版本,功能在派生類中最終調用虛成員函數。

原因是基類構造函數(析構函數)在派生類的構造函數(析構函數)之前(之後)被執行。這意味着表示派生類的對象的部分僅僅是尚未(不再是)現有。並且在不存在的對象上調用成員函數是不可能的。

您將需要實現的一種形式兩階段建設爲了做自己想做的(未內置到語言)。通常這是通過具有包裝類首先完全構造一個Sub對象,然後才調用parse()就可以了。

+1

+1,簡而言之,任何類的構造函數中的* this *指向相同的類,而不管它從何處被調用。 –

+0

你爲什麼不直接在你的回答中使用我的鏈接? http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6 – mkb

+0

@Als:儘管他的確是一個正確的陳述,但對於新手來說,這也是相當難以理解的。 ':)' – sbi

2

這裏的要點是,你不能在構造函數中調用純虛函數,即使你要提供一個實現,只有基類的實現將在構造函數中使用。

原因很簡單,Base的構造函數是在Sub的開頭執行的,所以對Sub的任何虛函數調用都會在不完整的對象上調用。

一般來說,沒有解決方案:您無法在構造函數或析構函數中調度虛函數。

2

虛擬函數的解析無法找到尚未構建的類 中的函數。 (形式上: 對象的動態類型是正在運行的構造函數或析構函數的對象。)所以 您需要某種後構造函數,該語言不支持 。

您可以通過使用一個虛擬的參數,它調用 你在析構函數所需要的功能,例如排序工作圍繞它的:

class Base 
{ 
public: 
    class PostConstructor 
    { 
     Base* owner; 
     friend class Base; 
    public: 
     PostConstructor() : owner(NULL) {} 
     ~PostConstructor() { owner->parse(); } 
    }; 
    Base(PostConstructor const& helper) 
    { 
     helper.owner = this; 
    } 
}; 

class Derived : public Base 
{ 
public: 
    Derived(PostConstructor const& helper = PostConstructor()) 
     : Base(helper) 
    { 
    } 
}; 

或者如果該類參數:

class Base 
{ 
    std::string name; 
public: 
    class PostConstructor 
    { 
     Base* owner; 
     std::string arg; // for example... 
     friend class Base; 
    public: 
     PostConstructor(std::string const& arg) // implicit conversion!! 
       : owner(NULL), arg(arg) {} 
     ~PostConstructor() { owner->parse(); } 
     operator std::string() const { return arg; } 
    }; 
    Base(PostConstructor const& helper) 
     : name(helper) 
    { 
     helper.owner = this; 
    } 
}; 

class Derived : public Base 
{ 
public: 
    Derived(PostConstructor const& helper) 
     : Base(helper) 
    { 
    } 
}; 

這是可行的,因爲PostConstructor將會是一個臨時的,被破壞的 在完整表達式的末尾(當Derived已經完全被 構造時)。

在這兩種情況下:派生類必須合作(但如果他們不這樣做,它們不會被編譯爲 )。如果在更復雜的表達式中構造類 ,該技巧也可能失敗。東西 這樣的:

Derived(...).someFunction(); 

在這種情況下,充分表達年底將返回 從someFunction之後,這可能是有點晚了打電話parse()

儘管有侷限性,但我發現這種情況很有用。

+1

建設性的批評。你的答案和「解決方法」是正確的,但對於非專業程序員來說似乎很複雜。 – umlcat

+0

@umlcat解決方法肯定有一些缺點,並不是一個通用的解決方案。這是我期望任何C++在看到它時能夠理解的東西(提供解釋我在發佈的文本部分中放置的內容的註釋)。然而,仍然有人學習C++可能會遇到問題。 –

0

抽象或純方法是虛方法的一個特例(所有抽象或純方法都是虛擬的)。

我以前的答案是錯誤的,因爲我忽略了構造函數。 C++上的構造函數不是虛擬的,並且不允許在構造函數內調用虛擬(抽象而非抽象方法)。如果你從另一個不是構造函數的方法調用一個非抽象的overrided「parse」,那就沒問題。

問題不在於其方法的抽象,它是從構造函數中調用的。

#include <conio> 

class Base 
{ 
public: 
    // a constructor: 
    Base(); 

    // "virtual and abstract" method: 
    virtual void parse() = 0; 

    // "virtual non abstract" method: 
    virtual void hello(); 
}; 

// Error: you cannot call a virtual method from a constructor, 
// wheter is abstract or not: 
Base::Base() 
{ 
    // error: 
    parse(); 

    // error: 
    hello(); 
} 

Base::hello() 
{ 
    cout << "Hello World\n"; 
} 

class Sub : public Base 
{ 
public: 
    Sub(); 

    // forgot "virtual" here, 
    // other languages use "override" instead, here: 
    virtual void parse(); 
    // another "overriden" methods: 
    virtual void parse(); 
}; 

// right: its important to call the base constructor, 
// in this case: 
Sub::Sub() : Base() 
{ 
    // ... 
} 

void Sub::parse() 
{ 
    // DO stuff 
} 

int main() 
{ 
    Base *MyBaseObject = new Base(); 
    MyObject->parse();  

    Sub *MyObject = new Sub(); 
    MyObject->parse(); 

    return 0; 
} 

這是一個解決方法。要調用一個虛擬的方法, 就像是從構造方法中調用,宣佈了新的方法,它的構造後立即叫:

#include <conio> 

class Base 
{ 
public: 
    // a constructor: 
    Base(); 

    // a "postconstructor" or "pseudoconstructor" 
    virtual void create(); 

    // "virtual and abstract" method: 
    virtual void parse() = 0; 

    // "virtual non abstract" method: 
    virtual void hello(); 
}; 

// Error: you cannot call a virtual method from a constructor, 
// wheter is abstract or not: 
Base::Base() 
{ 
    // no virtual methods called here, 
    // wheter abstract or not 
} 

// its not a real constructor, just a virtual method: 
void Sub::create() 
{ 
    // ... 
} 
Base::hello() 
{ 
    cout << "Hello World\n"; 
} 

class Sub : public Base 
{ 
public: 
    Sub(); 

    virtual void create(); 

    // forgot "virtual" here, 
    // other languages use "override" instead, here: 
    virtual void parse(); 
    // another "overriden" methods: 
    virtual void parse(); 
}; 

// right: its important to call the base constructor, 
// in this case: 
Sub::Sub() : Base() 
{ 
    // ... 
} 

// its not a real constructor, just a virtual method: 
void Sub::create() : create() 
{ 
    parse(); 
} 

void Sub::parse() 
{ 
    // DO stuff 
} 

int main() 
{ 
    // this commented code, wont work 
    /* 
    Base *MyBaseObject = new Base(); 
    MyObject->create();  
    MyObject->parse();  
    */ 

    // calling "pseudo-constructor", 
    // just after real constructor 
    Sub *MyObject = new Sub(); MyObject->create(); 
    MyObject->parse(); 

    return 0; 
} 

我的錯誤道歉。