簡體   English   中英

為什么鏈接器在虛擬情況下給出錯誤但在非虛擬情況下卻沒有?

[英]Why does the linker give me an error in the virtual case but not in the non-virtual case?

當我有這樣的事情:

class A
{
  virtual void rat();
};

class B : public A
{
  virtual void rat() { ; } //implemented!
};

int main(int argc, char **argv)
{
  A *a = new B;
  delete a;
}

我收到鏈接器錯誤:

除非我讓基礎老鼠變成純粹的虛擬。

但是,當我有這個:

class A
{
  public:
  void rat();
};

int main(int argc, char **argv)
{
  A a;
}

這編譯很好,並沒有給我一個未定義的引用鏈接錯誤,除非我明確嘗試在我的main( a.rat(); )中調用鼠函數。 未實現的基類虛函數的規則是什么,然而,在第一個失敗的代碼片段中,它是在派生類中實現的?

當兩個類都定義虛函數時,C ++編譯器需要為類AB構建vtable 要構建A的vtable,編譯器需要A::rat() - 這是引用的來源。

A沒有虛函數時,從任何地方都沒有引用A::rat ,因此不會出現編譯錯誤。

我相信你知道,你可以通過使A::rat成為一個純虛擬來修復這個錯誤,從而為vtable提供所需的值(在這種情況下,值為零)。

每個非純虛函數都需要實現。

class A 
{ 
   public: void rat(); 
};

int main(int argc, char **argv) 
{ 
  A a; 
}

上面的代碼完全是一個不同的場景。 在您不調用非純虛函數的情況下,您既不會收到編譯器/鏈接器錯誤,因為盡管它在類定義中聲明,但您根本不會調用它。

現在在下面的代碼,編譯器只檢查是否有一個名為成員函數rat()或不A

A a;
a.rat();   // Compiler passes. But linker bombs.

現在你應該得到一個鏈接器錯誤。

因為A::rat() 不是純虛擬的,所以它必須具有實現。

標准法律術語是使用ODR :必須定義使用ODR的函數,否則它是錯誤的。

將函數標記為ODR使用的規則非常復雜,基本上它意味着函數以某種方式使用。 在第二個示例中,未使用該函數,因此不需要。

有一個特別的警告virtual功能(除非它是純粹的) 總是被認為是ODR使用的。

因為C ++標准要求它實現。 來自C ++03§10.3/ 8:

在類中聲明的虛函數應在該類中定義或聲明為純(10.4),或兩者兼有; 但不需要診斷(3.2)。

因此,您需要將其聲明為純(在右括號后面加上= 0后綴)或定義其實現。

至於為何在非虛擬情況下不調用函數時沒有出現錯誤,請參閱C ++03§3.2/ 2-3(強調我的)

2)表達式可能被評估,除非它出現在需要整數常量表達式的地方(見5.19),是sizeof運算符的操作數(5.3.3),或者是typeid運算符的操作數,表達式沒有指定多態類型的左值(5.2.8)。 如果對象或非重載函數的名稱出現在可能已評估的表達式中,則使用該函數。 如果它不是純粹的,則使用虛擬成員函數。 [...]

3)每個程序應該只包含該程序中使用的每個非內聯函數或對象的一個​​定義 ; 無需診斷。 該定義可以在程序中明確顯示,可以在標准或用戶定義的庫中找到,或者(在適當的時候)隱式定義(見12.1,12.4和12.8)。 內聯函數應在每個使用它的翻譯單元中定義。

因此,在非虛擬情況下,如果未使用,則不需要定義。 但是在非純虛擬情況下,即使它沒有在代碼中明確引用,它仍然被認為是由標准使用,因此它的定義是必需的。

另請參見如果不是純粹的虛擬成員函數?

暫無
暫無

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

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