2012-07-04 21 views
4

我知道在基類的構造函數中 - 調用虛方法時 - 調用基方法,而不是派生 - 請參閱Calling virtual functions inside constructors這是否正確:在構造Base對象之前調用Derived的虛方法?

我的問題與此主題有關。我只是想知道如果我在Derived類的構造函數中調用virtual方法會發生什麼 - 但是在構造Base部分之前。我的意思是打電話來評估基類構造函數參數的虛方法,見代碼:

class Base { 
public: 
    Base(const char* name) : name(name) { 
    cout << "Base():" << name << endl; 
    } 
    virtual const char* getName() { 
    cout << "Base::getName()" << endl; 
    return "Base"; 
    } 
protected: 
    const char* name; 
}; 

class Derived : public Base { 
public: 
    Derived() : Base(getName()) { 
    cout << "Derived():" << name << endl; 
    } 
    virtual const char* getName() { 
    cout << "Derived::getName()" << endl; 
    return "Derived"; 
    } 
}; 

int main() { 
    Derived d; 
} 

編譯器G ++(版本4.3.x-4.5X版本)輸出爲:

Derived::getName() 
Base():Derived 
Derived():Derived 

不過我期望:

Base::getName() 
Base():Base 
Derived():Base 

這不會看錯了 - 但考慮到這個例子,它產生segmentation fault

class Derived : public Base { 
public: 
    Derived() : Base(getName()), name(new string("Derived")) { 
    cout << "Derived():" << Base::name << endl; 
    } 
    virtual const char* getName() { 
    cout << "Derived::getName()" << endl; 
    return name->c_str(); 
    } 
private: 
    string* name; 
}; 

請問這是正確的g ++行爲嗎? C++標準對此有何評論?也許這是未定義的行爲?

[UPDATE1] 我考慮到羅伯特和奧利的答案 - 我改變了我的第一個例子。然後getName()被稱爲「虛擬」 - 它會產生分段錯誤。請回答我的問題到這個部分。

const char* virtualGetName(Base* basePtr) 
{ 
    return basePtr->getName(); 
} 

class Derived : public Base { 
public: 
    Derived() : Base(virtualGetName(this)) { 
    cout << "Derived():" << Base::name << endl; 
    } 
    virtual const char* getName() { 
    cout << "Derived::getName()" << endl; 
    return "Derived"; 
    } 
}; 

回答

11

您的所有示例都顯示未定義的行爲。 C++語言標準狀態(C++ 11§12.6.2/ 13):

可以爲正在構建的對象調用成員函數(包括虛擬成員函數)。類似地,正在構建的對象可以是typeid運營商的操作數或dynamic_cast的操作數。

然而,如果這些操作以構造函數初始化程序執行(或在直接或間接調用從構造函數-初始化功能)中的所有MEM-初始化爲基類已完成,則前操作結果未定義。

要調用從初始化列表的成員函數getName()Derived類構造函數(該構造函數- 初始化)。此成員函數調用必須在Base的初始化程序完成之前發生(mem初始化程序對於Base),因爲它是初始值設定程序本身的參數。

因此,行爲是不確定的。

通常,Never Call Virtual Functions during Construction or Destruction

+0

我在我的問題中添加了更新,我相信更多的不像您的回答 - 但現在我真的不知道爲什麼在最後一個示例中出現了「段錯誤」。似乎並不總是方法被虛擬調用。並且vpointer到Derived :: vtable被設置在ctor體的開始處(在{之後)。 – PiotrNycz

+0

我已經更新了我的答案。我前兩次嘗試解釋這種行爲是災難性的失敗。我相當有信心,這個最新的解釋是正確的:-)。 –

+0

@PiotrNycz:「爲什麼分段錯誤?」行爲是未定義的。分段錯誤是[但一個可能的結果](http://stackoverflow.com/a/1553407/140719)調用未定義的行爲。 – sbi

1

我只是想知道,如果我調用派生類的構造虛方法會發生什麼 - 但建設基地部分之前。

它可能看起來像你這樣做,但你不是。

在你的第一個例子中,Derived::getName()不依賴於this,所以方法調用起作用。在你的第二個例子中,Derived::getName()的確取決於this。由於this->name尚未設置,它指向一個未定義的位置,並給你一個段錯誤。

this->name尚未設置,因爲Derived構造函數所做的第一件事是調用Base構造函數。如果指定要傳遞給Base構造函數的參數,它將首先處理它們。然後通過調用它們的構造函數來實例化類的成員變量。初始化程序列表可用於將參數傳遞給這些構造函數,但它不能改變它們被調用的順序。這一步是name被初始化的地方。最後,執行構造函數Derived的主體。

+0

如果這是C++標準定義的行爲,你能回答嗎?或者它是未定義的,並且特定於g ++ ... – PiotrNycz

+0

在第一個示例中,Derived :: getName()不依賴於this這個...沒關係。這是UB。是否涉及這個只是決定可能的結果。 – sbi

相關問題