链接时未定义C ++虚函数 - 为什么?

时间:2011-02-12 20:23:28

标签: c++ inheritance undefined virtual-method

我在使用C ++中的虚函数时遇到了一些麻烦,我可能会在构造函数中滥用它们。问题是当将组件库lib(由我编写)链接到我的最终可执行文件时,虚拟函数被标记为未定义,即使我已经为它编写了实现,并将其链接。

我有以下课程:

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

我在这里做错了什么?请询问您是否需要更多信息,希望尽可能缩短它!

3 个答案:

答案 0 :(得分:18)

您正在从基类构造函数中调用虚函数。在构建和销毁过程中有一些特殊的虚拟功能发送规则:

实际上,当基类UdpConnection的构造函数正在执行时,对象的动态类型为UdpConnection,而不是UdpClient,因此虚函数的最终覆盖是选中的是UdpConnection,而不是派生程度最高的类UdpClient

这意味着当您在initSysHandlers()构造函数中调用UdpConnection时,它会被UdpConnection::initSysHandlers()调用,而不是在最派生类中的覆盖。因为您没有提供UdpConnection::initSysHandlers()的定义,所以会出现链接器错误。

专家建议您应该"Never Call Virtual Functions during Construction or Destruction"

答案 1 :(得分:2)

不要在施工时调用虚拟功能。构造从基类到派生类。所以构造函数UdpConnection::UdpConnection还不知道该类已被继承。此时尚未形成派生类的vtable,并且不知道要调用的重载函数的地址。

答案 2 :(得分:0)

您永远不应该在构造函数或析构函数中调用虚函数,因为它们没有预期的行为。在构造函数返回之前,该对象尚未完全创建。

在派生类构造函数之前调用基类构造函数;因此,首先创建和定义基类数据成员和函数。因此,在您的情况下,UdpConnection ctor将尝试调用UdpConnection :: initSysHandlers,而不是UdpClient :: initSysHandlers,因为它尚未创建。由于UdpConnection :: initSysHandlers是纯虚拟的,因此在那时它是未定义的。