[英]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.