簡體   English   中英

為什么在下面的第一個示例和第二個示例中,VS2013為什么分別使用非虛擬和虛擬調用?

[英]Why does VS2013 use a non-virtual and a virtual call, respectively, in the first and the second examples below?

第一個示例:純虛函數foo()Base構造函數中通過非虛調用被調用。 這就解釋了為什么代碼可以正常執行,即,它不會像第二個示例那樣中止。

#include <iostream>
struct Base {
    Base() { foo(); }
    virtual void foo() = 0;
};

void Base::foo() { std::cout << "Base::foo()\n"; }

struct Derived : Base { void foo() { std::cout << "Derived::foo()\n"; } };

int main()
{
    Derived d;
}

第二個示例:在這里,純虛擬函數foo()Base ctor中也被調用,但是有一個虛擬調用,並且代碼因R6025 - pure virtual function call中止。

#include <iostream>

struct Base {
    Base() { call_foo(); }
    virtual void foo() = 0;
    void call_foo() { foo(); }
};

void Base::foo() { std::cout << "Base::foo()\n"; }

struct Derived : Base { void foo() { std::cout << "Derived::foo()\n"; } };

int main()
{
    Derived d;
}

我從§10.4/ 6知道,從構造函數或析構函數調用的純虛函數被視為未定義行為。 但是我很好奇,對於這兩個片段中對foo()的不同調用,可能有什么合理的解釋?

在第一種情況下,直接從構造函數中調用函數,動態類型在編譯時是已知的,因此不需要虛擬調度。 編譯器可以直接調用Base::foo

在第二種情況下,從另一個函數調用它,該函數可以隨時從Base派生的任何類型調用,動態類型在編譯時未知,因此必須進行虛擬分派。

如您所說,這是不確定的行為; 原則上,無論哪種情況都可能發生。 我希望在第一種情況下出現編譯器警告(GCC給出了警告); 但是在第二種情況下,該錯誤只能在運行時(如果有的話)才能檢測到。

好吧, 不確定的行為意味着任何事情都可能發生; 包括可能令人驚訝的事件,例如似乎起作用或引發異常。

現在,假設VS選擇在構建或銷毀過程中發出對純虛擬的調用時中止; 我懷疑第一個行為(調用Base::foo )實際上是一個錯誤(根據規范)。 如果將被調用的方法純化 ,使函數調用虛擬化而錯過無法編譯的特殊情況的代碼將變得微不足道。

正如其他人所說,這是未定義的行為。 但是(可能)發生的是,在第一種情況下(構造函數中的調用),編譯器知道對象的動態類型(因為它始終是構造的類型),因此生成調用的方式與它將用於調用任何非虛擬函數。 (如果您未能提供定義,我想鏈接器會抱怨的。)在第二種情況下,您從另一個函數中調用了該函數。 那時,編譯器無法知道對象在運行時將具有的動態類型。 可以在完全構造的對象上調用該函數,該對象甚至可能是此源文件中不存在的派生類型。 由於編譯器無法靜態確定對象在運行時將具有的動態類型,因此它必須通過虛擬函數表(或其用於解析動態分配的任何方法)生成調用,但是VS和我所認識的其他所有人一樣,都使用vptr來vtable)。 由於以這種方式調用純虛函數是未定義的行為,因此編譯器將指針指向vtable中的錯誤處理例程。

請注意,在這種情況下,編譯器可能已經看到, Base構造函數中的調用將以導致未定義行為的方式解析,並生成會觸發錯誤的代碼。 或者...由於Base所有虛函數都是純虛函數,因此編譯器可能知道在構造過程中無法調用the,甚至不費心為Base創建單獨的vtable,可能會初始化vptr指向vtable在調用Base的構造函數之前Derived的。 您不能指望任何此類情況。

暫無
暫無

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

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