我發現了這個有關虛函數和DLL的小問題,並且認爲我會分享我對它的瞭解。非導出的虛函數導致LNK2001在其他項目的派生類中
假設您有兩個項目,名爲Alpha
和Bravo
。 Alpha
被構建爲DLL,引用了Bravo
。現在,在Alpha
,你有基類:
頭文件(Alpha.h)
#pragma once
#if defined(EXPORT_ALPHA)
#define ALPHA_API __declspec(dllexport)
#else
#define ALPHA_API __declspec(dllimport)
#endif
class BaseClass
{
public:
ALPHA_API BaseClass();
ALPHA_API virtual ~BaseClass();
virtual void Foo();
};
cpp文件:(Alpha.cpp)
#include "Alpha.h"
#include <cstdio>
BaseClass::BaseClass() {}
BaseClass::~BaseClass() {}
void BaseClass::Foo()
{
printf("Foo\n");
}
然後,在Bravo
,你有派生類和main(稱之爲main.cpp):
#include "Alpha.h"
#include <cstdio>
class DerivedClass : public BaseClass
{
public:
DerivedClass() : BaseClass() {}
virtual ~DerivedClass() {}
};
int main()
{
DerivedClass* derived = new DerivedClass();
printf("Created instance of derived class.\n");
delete derived;
return 0;
}
現在,Alpha
構建成功地生成它的DLL,然後繼續它的快樂方式。但是,然後,你去建立Bravo
,你得到LNK2001 - unresolved external symbol BaseClass::Foo()
,即使你從來沒有真正使用它。
那麼,發生了什麼?如果我們從未撥打Foo()
,爲什麼它會生成鏈接器錯誤?
構造函數是內聯的。嘗試刪除構造函數的內聯定義,並在鏈接到DLL的翻譯單元中顯式定義構造函數。在我看來,因爲構造函數是內聯定義的,當Bravo編譯時它會嘗試構建基類,所以它需要對基本虛方法的引用。雖然這看起來應該仍然有效,但虛擬基本方法也是內聯的。 –
@SamVarshavchik是的,但編譯器/鏈接器不需要內聯它,即使添加了inline關鍵字。另外,這些函數是可以內聯的,只是因爲我寫了足夠短的內容才能用作一個沒有額外代碼混亂的例子。 –
我沒有看到編譯器***沒有選項內聯一切。當翻譯單元包含此頭文件時,編譯器無法確定其他翻譯單元是否也會看到相同的類聲明,並因此發出構造函數和虛方法,因爲它需要構造虛函數調度表。因此,在我看來編譯器必須在編譯「Bravo」時嵌入所有內容(這裏,「inline」意思是將函數的代碼作爲翻譯單元的一部分發布,而不是在某個調用點實際內聯)。 –