【问题标题】:C++ virtual function undefined at link time - why?C++ 虚函数在链接时未定义 - 为什么?
【发布时间】:2025-12-03 17:45:02
【问题描述】:

我在 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()
{

}

如您所见,我在构造函数中调用了一个虚函数。据我所知,这应该没问题,因为我知道我的子类构造函数不会被调用,所以我不能使用任何实例变量,但我只是将一些子类特定项添加到标准::地图。

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

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

【问题讨论】:

    标签: c++ inheritance undefined virtual-functions


    【解决方案1】:

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

    【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 调用是否具有预期行为取决于您期望的行为。
    • 即使行为恰好看起来是预期的,它仍然是错误的。
    【解决方案3】:

    您正在从基类构造函数调用虚函数。虚函数在构造和销毁过程中的调度有特殊的规则:

    实际上,当基类UdpConnection的构造函数在执行时,对象的动态类型是UdpConnection,而不是UdpClient,所以选择的虚函数的最终覆盖是@的那个987654325@,不是派生最多的类,UdpClient

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

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

    【讨论】:

    • 好的,谢谢!以为是这样的,但是错误消息太不具描述性了(因为我只是认为我不能使用子类的任何成员)。有时我希望编译器会添加更多描述性提示!
    • @Max:是的,如果你从构造函数调用虚函数,一些编译器会警告你。其他编译器不太有用。
    • 我错误地点击了向下箭头,现在它已修复.. 我终于获得了我的评论家徽章 xD
    • @ak:哦;哈!没问题。 :-) 感谢您的提醒。