簡體   English   中英

什么時候在構造函數中調用虛函數是安全的

[英]When is it safe to call a virtual function in a constructor

我有一些代碼,我真的想從構造函數調用虛方法。 我知道這被認為是不安全的,而且我對對象構造有足夠的了解,也可以理解為什么. 我也沒有遇到這些問題 目前我的代碼正在運行,我認為應該沒問題,但我想確定一下。

這是我在做什么:

我有一些類層次結構,並且有一個普通的公共函數,它像往常一樣轉發到私有虛擬方法。 但是,我確實想在構建對象時調用此公共方法,因為它將所有數據填充到對象中。 我絕對確定這個虛調用來自葉類,因為從類層次結構的任何其他部分使用這個虛方法根本沒有意義。

所以在我看來,一旦我進行虛擬調用,對象創建就應該完成,一切都應該沒問題。 還有什么可能出錯的嗎? 我想我必須用一些大注釋來標記邏輯的這一部分,以解釋為什么這個邏輯永遠不應該移動到任何基類,即使它看起來可以移動。 但是除了其他程序員的愚蠢之外,我應該沒問題,不是嗎?

在構造函數或析構函數中調用任何非抽象虛函數是絕對安全的! 但是,它的行為可能會令人困惑,因為它可能不會執行預期的操作。 在執行類的構造函數時,對象的靜態和動態類型就是構造函數的類型。 也就是說,虛函數永遠不會被分派到進一步派生類的覆蓋。 除此之外,虛擬分派實際上是有效的:例如,當通過基類指針或引用調用虛擬函數時,正確分派到當前正在構造或銷毀的類中的覆蓋。 例如(可能充斥着拼寫錯誤,因為我目前無法使用此代碼):

#include <iostream>
struct A {
    virtual ~A() {}
    virtual void f() { std::cout << "A::f()\n"; }
    void g() { this->f(); }
};
struct B: A {
    B() { this->g(); } // this prints 'B::f()'
    void f() { std::cout << "B::f()\n"; }
};
struct C: B {
    void f() { std::cout << "C::f()\n"; } // not called from B::B()
};

int main() {
    C c;
}

也就是說,如果您不希望將虛函數分派給進一步的派生函數,則可以在類的構造函數或析構函數中直接或間接調用虛函數。 你甚至可以這樣做,只要定義了給定的類中的虛函數是抽象的。 但是,將未定義的抽象函數分派到將導致運行時錯誤。

當調用構造函數時,類被設置為該類的實例,而不是派生類。 不能從基構造函數調用派生類的虛函數。 當您到達最派生類的構造函數時,所有虛函數都應該可以安全調用。

如果您希望確保某人不會進行錯誤調用,請在基類中定義虛函數,並在調用時斷言和/或拋出異常。

規則並不是說您需要在葉類中,而是要意識到當您從Foo::Foo(..)進行成員調用時,該對象正是一個Foo ,即使它正在前往是一個Bar (假設Foo派生自Bar並且您正在構建一個Bar實例)。 那是 100% 可靠的。

否則,成員是虛擬的這一事實並不是那么重要。 非虛函數也存在其他陷阱:如果您要調用一個假設對象已完全構造但在構造函數中調用它的虛方法或非虛方法,那么您還需要有問題。 這些只是很難確定的情況,因為不僅你調用的函數必須是好的,它調用的所有函數也必須是好的。

聽起來您沒有問題,這只是容易出現錯誤的地方之一。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM