簡體   English   中英

為什么在沒有主體的情況下調用純虛擬方法不會導致鏈接器錯誤?

[英]Why does calling calling a pure virtual method without body does not result in linker error?

我今天遇到了很奇怪的情況。 在接口構造函數中直接調用純虛方法時,出現未定義的引用錯誤。

class Interface
{
public:    
    virtual void fun() const = 0; 
    Interface(){ fun(); }
};

class A : public Interface
{
public:
    void fun() const override {};
};

int main()
{
    A a;
}

結果是:

prog.cc: In constructor 'Interface::Interface()':
prog.cc:5:22: warning: pure virtual 'virtual void Interface::fun() const' called from constructor
5 |     Interface(){ fun(); }
  |                      ^
/tmp/ccWMVIWG.o: In function `main':
prog.cc:(.text.startup+0x13): undefined reference to `Interface::fun() const'
collect2: error: ld returned 1 exit status

但是,將對fun()的調用包裝在不同的方法中,如下所示:

class Interface
{
public:    
    virtual void fun() const = 0; 
    Interface(){ callfun(); }
    virtual void callfun()
    {
        fun();
    }
};

class A : public Interface
{
public:
    void fun() const override {};
};

int main()
{
    A a;
}

編譯就很好,並且(顯然)由於純虛擬調用錯誤而崩潰。 我已經在最新的GCC 8.2.0和9.0.0和Clang 8.0.0上進行了測試。 在這些情況中,只有GCC在第一種情況下會產生鏈接器錯誤。

Wandbox鏈接顯示了完整的工作示例,並顯示以下錯誤:

編輯:我被標記為重復,但我不確定如何重復此問題。 我知道它們與調用純虛擬方法(從構造函數或其他方法)的危險沒有任何關系。

我試圖理解為什么編譯器在一種情況下允許此調用,而在另一種情況下卻不允許這樣做,Adam Nevraumont對此進行了很好的解釋。

EDIT2:看來,即使callFun不是虛擬的,它仍會以某種方式阻止GCC取消虛擬化和內聯fun呼叫。 請參閱以下示例:

class Interface
{
public:    
    virtual void fun() const = 0; 
    Interface(){ callfun(); }
    void callfun()
    {
        fun();
    }
};

class A : public Interface
{
public:
    void fun() const override {};
};

int main()
{
    A a;
}

您不是在調用純虛函數,而是在vtable中對該函數的虛函數表中的當前條目進行查找。

碰巧的是,那時它只是一個純虛函數,因此您由於UB而崩潰。

在第一種情況下,您會收到鏈接器錯誤,因為gcc正在取消ctor中對fun的調用的虛擬化。 fun的非虛擬化調用直接調用純虛擬方法。 這是可能的,因為在構造Interface ,編譯器會知道虛擬功能表的狀態(尚未對其進行派生的類修改)。

在第二種情況下,編譯器可以取消ctor對callFun的調用的callFun 但是從callFun內部進行的對fun的調用callFun虛擬化,因為callFun可以通過ctor之外的其他方法進行調用。 在一般情況下 ,將其去虛擬化是不正確

在這種特定情況下,如果編譯器先對callFun進行callFun ,然后對其進行內聯,則可以對內聯副本中的fun進行虛擬化。 但是編譯器不會執行此操作,因此不會發生虛擬化。

順便說一句,您可以實現該純虛函數,並使您提供的每個示例都鏈接並正常運行。

void Interface::fun() const {}

鏈接到的任何.cpp文件中的任何位置都將使您的代碼鏈接,並且無論如何都是正確的。 純虛擬語言在C ++中並不表示“沒有實現”,而只是表示“派生的類必須提供重寫,並且對我而言,沒有實現是合法的”。

暫無
暫無

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

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