Здесь много спекуляций в разных ответах. Ниже я приведу довольно минимальный код, который воспроизводит эту ошибку, и объясню, почему она возникает.
Довольно минимальный код для воспроизведения этой ошибки
IBase.hpp
#pragma once
class IBase {
public:
virtual void action() = 0;
};
Derived.hpp
#pragma once
#include "IBase.hpp"
class Derived : public IBase {
public:
Derived(int a);
void action() override;
};
Derived.cpp
#include "Derived.hpp"
Derived::Derived(int a) { }
void Derived::action() {}
myclass.cpp
#include <memory>
#include "Derived.hpp"
class MyClass {
public:
MyClass(std::shared_ptr<Derived> newInstance) : instance(newInstance) {
}
void doSomething() {
instance->action();
}
private:
std::shared_ptr<Derived> instance;
};
int main(int argc, char** argv) {
Derived myInstance(5);
MyClass c(std::make_shared<Derived>(myInstance));
c.doSomething();
return 0;
}
Вы можете скомпилировать это с помощью GCC следующим образом:
g++ -std=c++11 -o a.out myclass.cpp Derived.cpp
Теперь вы можете воспроизвести ошибку, удалив = 0
в IBase.hpp. Я получаю эту ошибку:
~/.../catkin_ws$ g++ -std=c++11 -o /tmp/m.out /tmp/myclass.cpp /tmp/Derived.cpp
/tmp/cclLscB9.o: In function `IBase::IBase(IBase const&)':
myclass.cpp:(.text._ZN5IBaseC2ERKS_[_ZN5IBaseC5ERKS_]+0x13): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o: In function `IBase::IBase()':
Derived.cpp:(.text._ZN5IBaseC2Ev[_ZN5IBaseC5Ev]+0xf): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o:(.rodata._ZTI7Derived[_ZTI7Derived]+0x10): undefined reference to `typeinfo for IBase'
collect2: error: ld returned 1 exit status
Объяснение
Обратите внимание, что приведенный выше код не требует каких-либо виртуальных деструкторов, конструкторов или любых других дополнительных файлов для успешной компиляции (хотя они должны быть у вас).
Способ понять эту ошибку следующим образом:
Линкер ищет конструктора IBase. Это понадобится для конструктора Derived. Однако поскольку Derived переопределяет методы из IBase, к нему прикреплен vtable, который будет ссылаться на IBase. Когда компоновщик говорит «неопределенная ссылка на vtable для IBase», это в основном означает, что Derived имеет vtable ссылку на IBase, но он не может найти какой-либо скомпилированный объектный код IBase для поиска. Итак, суть в том, что у класса IBase есть объявления без реализаций. Это означает, что метод в IBase объявлен как виртуальный, но мы забыли пометить его как чисто виртуальный ИЛИ предоставить его определение.
Разводной наконечник
Если ничего не помогает, то одним из способов отладки этой ошибки является создание минимальной программы, которая выполняет компиляцию, а затем продолжает изменять ее, чтобы она достигла желаемого состояния. В перерывах продолжайте компиляцию, чтобы увидеть, когда она начнет давать сбой.
Примечание по ROS и системе сборки Catkin
Если вы компилировали вышеуказанный набор классов в ROS с использованием системы сборки catkin, то вам понадобятся следующие строки в CMakeLists.txt:
add_executable(myclass src/myclass.cpp src/Derived.cpp)
add_dependencies(myclass theseus_myclass_cpp)
target_link_libraries(myclass ${catkin_LIBRARIES})
Первая строка в основном говорит о том, что мы хотим создать исполняемый файл с именем myclass, а код для его сборки можно найти в следующих файлах. Один из этих файлов должен иметь main (). Обратите внимание, что вам не нужно указывать файлы .hpp где-либо в CMakeLists.txt. Также вам не нужно указывать Derived.cpp в качестве библиотеки.