2011-02-12 73 views
7

我在使用C++中的虛函數時遇到了一些麻煩,我可能會在構造函數中濫用它們。問題是,當我將一個組件lib(由我編寫)鏈接到我的最終可執行文件時,即使我已經爲它編寫了一個實現並將其鏈接,虛擬函數仍被標記爲未定義。鏈接時未定義C++虛函數 - 爲什麼?

我有以下類:

template<class BufferType, class ConnectionType, class HandlerType> 
class UdpConnection 
{ 
public: 
UdpConnection(size_t dispatchCount) : service(), 
     listener(service), 
     pool(dispatchCount), sysMsgHandlers(), 
     bufferPool(), buffers() 
    { 
     assert(dispatchCount > 0); 
     initBuffers(dispatchCount); 
     initSysHandlers(); 
    } 
protected: 
    virtual void initSysHandlers() = 0; 
} 

在我的子類:

class UdpClient : public UdpConnection<SyncBufferHandler, UdpClient, ClientNetworkHandler> 
{ 
    protected: 
     void initSysHandlers(); 
} 

類和子類的源文件:

void UdpClient::initSysHandlers() 
{ 

} 

正如你所看到的,我打電話一在我的構造函數中的虛函數。據我所知,這應該沒問題,因爲我知道我的子類構造函數不會被調用,所以我不能使用任何實例變量,但我只需將一些子類特定項添加到的std ::地圖。

Linking CXX static library libnetwork.a 
[ 75%] Built target network                       
Scanning dependencies of target testclient 
[ 87%] Building CXX object CMakeFiles/testclient.dir/src/test/testclient.cpp.o           
Linking CXX executable testclient                      
src/network/libnetwork.a(udpclient.cpp.o): In function `voip::network::UdpConnection<voip::network::client::SyncBufferHandler, voip::network::client::UdpClient, voip::network::client::ClientNetworkHandler>::UdpConnection(unsigned long)': 
udpclient.cpp:(.text._ZN4voip7network13UdpConnectionINS0_6client17SyncBufferHandlerENS2_9UdpClientENS2_20ClientNetworkHandlerEEC2Em[voip::network::UdpConnection<voip::network::client::SyncBufferHandler, voip::network::client::UdpClient, voip::network::client::ClientNetworkHandler>::UdpConnection(unsigned long)]+0x10d): undefined reference to `voip::network::UdpConnection<voip::network::client::SyncBufferHandler, voip::network::client::UdpClient, voip::network::client::ClientNetworkHandler>::initSysHandlers()' 
collect2: ld returned 1 exit status 

我在做什麼錯在這裏?請詢問您是否需要更多信息,希望儘可能縮短!

回答

18

您正在調用基類構造函數的虛函數。存在用於虛擬功能結構和破壞時的調度的特殊規則:

有效地,當基類UdpConnection正在執行的構造中,動態對象的類型是UdpConnection,不UdpClient,因此的最終超控器所選虛擬功能是UdpConnection的虛擬功能,不是最派生的類,UdpClient

這意味着當您在UdpConnection構造函數中調用initSysHandlers()時,它將被調用,永遠是時間,而不是大多數派生類中的重寫。由於您沒有提供UdpConnection::initSysHandlers()的定義,因此會出現鏈接器錯誤。

專家的意見是,你應該"Never Call Virtual Functions during Construction or Destruction"

2

不要在施工時調用虛函數。構造從基類到派生類。所以構造函數UdpConnection::UdpConnection還不知道該類已被繼承。此時派生類的vtable尚未形成,並且調用的重載函數的地址未知。

+0

它實際上比「vtable還沒有形成」更復雜一些。有[一個有趣的例子涉及從初始化列表中調用虛函數](http://stackoverflow.com/questions/4804387/c-base-constructor-calling-with-parameter-that-will-be-constructed-in-在-DERIV)。 – 2011-02-12 20:38:45

+0

我仍然很確定這個問題與vtable有關。在鏈接過程中,當目標文件在統一的地址空間中組合在一起時會出現問題。希望有一天能夠找到更多關於C++內幕的知識。 – 2011-02-12 23:41:12

0

您不應該在構造函數或析構函數中調用虛函數,因爲它們沒有預期的行爲。直到構造函數返回,該對象才被完全創建。

在派生類構造函數之前調用基類構造函數;因此首先創建並定義基類數據成員和函數。所以在你的情況下,UdpConnection ctor會嘗試調用UdpConnection :: initSysHandlers,而不是UdpClient :: initSysHandlers,因爲它尚未創建。由於UdpConnection :: initSysHandlers是純虛擬的,因此它在那時未定義。

+1

調用是否具有預期的行爲取決於您期望的行爲。 – 2011-02-12 20:32:00

相關問題