Виртуальная функция C ++ не определена во время соединения - почему? - PullRequest
7 голосов
/ 12 февраля 2011

У меня небольшие проблемы с использованием виртуальных функций в 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()
{

}

Как видите, я вызываю виртуальную функцию в своем конструкторе. Насколько я могу судить, это должно быть хорошо, так как я знаю, что мой конструктор подкласса не был вызван, поэтому я не могу использовать переменные экземпляра, но я просто добавляю несколько специфичных для подкласса элементов станд :: карта.

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 ]

18 голосов
/ 12 февраля 2011

Вы вызываете виртуальную функцию из конструктора базового класса. Существуют специальные правила для отправки виртуальных функций при строительстве и разрушении:

Фактически, когда выполняется конструктор базового класса UdpConnection, динамический тип объекта - UdpConnection, а не UdpClient, поэтому последний переопределитель выбранной виртуальной функции - тот, для которого UdpConnection, не самый производный класс, UdpClient.

Это означает, что когда вы вызываете initSysHandlers() в конструкторе UdpConnection, UdpConnection::initSysHandlers() вызывается всегда, а не переопределением в самом производном классе. Поскольку вы не предоставили определение UdpConnection::initSysHandlers(), вы получите ошибку компоновщика.

Совет эксперта заключается в том, что вам следует «Никогда не вызывать виртуальные функции во время строительства или разрушения» .

2 голосов
/ 12 февраля 2011

Не вызывать виртуальные функции во время строительства. Конструкция идет от базового класса к производному классу. Поэтому конструктор UdpConnection::UdpConnection еще не знает, что класс унаследован. На данный момент vtable для производного класса еще не сформирован, а адрес перегруженной функции для вызова неизвестен.

0 голосов
/ 12 февраля 2011

Вы никогда не должны вызывать виртуальные функции в конструкторе или деструкторе, поскольку они не имеют ожидаемого поведения.Объект не был полностью создан, пока конструктор не вернул.

Конструктор базового класса вызывается перед конструктором производного класса;поэтому члены и функции данных базового класса создаются и определяются первыми.Так что в вашем случае ctor UdpConnection попытается вызвать UdpConnection :: initSysHandlers, а не UdpClient :: initSysHandlers, потому что он еще не создан.Так как UdpConnection :: initSysHandlers является чисто виртуальным, в этой точке он не определен.

...