Cuando construyo mi programa C ++, recibo el mensaje de error
referencia indefinida a 'vtable ...
¿Cuál es la causa de este problema? ¿Cómo lo soluciono?
Sucede que recibo el error para el siguiente código (La clase en cuestión es CGameModule) y no puedo entender por qué es el problema. Al principio, pensé que estaba relacionado con olvidar dar una función virtual a un cuerpo, pero por lo que entiendo, todo está aquí. La cadena de herencia es un poco larga, pero aquí está el código fuente relacionado. No estoy seguro de qué otra información debo proporcionar.
Nota: El constructor es donde está ocurriendo este error, parece.
Mi código:
class CGameModule : public CDasherModule {
public:
CGameModule(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, const char *szName)
: CDasherModule(pEventHandler, pSettingsStore, iID, 0, szName)
{
g_pLogger->Log("Inside game module constructor");
m_pInterface = pInterface;
}
virtual ~CGameModule() {};
std::string GetTypedTarget();
std::string GetUntypedTarget();
bool DecorateView(CDasherView *pView) {
//g_pLogger->Log("Decorating the view");
return false;
}
void SetDasherModel(CDasherModel *pModel) { m_pModel = pModel; }
virtual void HandleEvent(Dasher::CEvent *pEvent);
private:
CDasherNode *pLastTypedNode;
CDasherNode *pNextTargetNode;
std::string m_sTargetString;
size_t m_stCurrentStringPos;
CDasherModel *m_pModel;
CDasherInterfaceBase *m_pInterface;
};
Hereda de...
class CDasherModule;
typedef std::vector<CDasherModule*>::size_type ModuleID_t;
/// \ingroup Core
/// @{
class CDasherModule : public Dasher::CDasherComponent {
public:
CDasherModule(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, ModuleID_t iID, int iType, const char *szName);
virtual ModuleID_t GetID();
virtual void SetID(ModuleID_t);
virtual int GetType();
virtual const char *GetName();
virtual bool GetSettings(SModuleSettings **pSettings, int *iCount) {
return false;
};
private:
ModuleID_t m_iID;
int m_iType;
const char *m_szName;
};
Que hereda de ...
namespace Dasher {
class CEvent;
class CEventHandler;
class CDasherComponent;
};
/// \ingroup Core
/// @{
class Dasher::CDasherComponent {
public:
CDasherComponent(Dasher::CEventHandler* pEventHandler, CSettingsStore* pSettingsStore);
virtual ~CDasherComponent();
void InsertEvent(Dasher::CEvent * pEvent);
virtual void HandleEvent(Dasher::CEvent * pEvent) {};
bool GetBoolParameter(int iParameter) const;
void SetBoolParameter(int iParameter, bool bValue) const;
long GetLongParameter(int iParameter) const;
void SetLongParameter(int iParameter, long lValue) const;
std::string GetStringParameter(int iParameter) const;
void SetStringParameter(int iParameter, const std::string & sValue) const;
ParameterType GetParameterType(int iParameter) const;
std::string GetParameterName(int iParameter) const;
protected:
Dasher::CEventHandler *m_pEventHandler;
CSettingsStore *m_pSettingsStore;
};
/// @}
#endif
qmake -project
y luegoqmake
) para generar uno nuevoMakefile
, es probable que se deba al error al usar Qt.Q_OBJECT
se copia externamente, pero aún no forma parte del archivo .pro, aunque se compila bien, no se vincula. Tenemos que agregar ese.h/.cpp
archivo al archivo .pro para poder hacerloqmake
.Respuestas:
Las preguntas frecuentes de GCC tienen una entrada:
fuente
nm -C CGameModule.o | grep CGameModule::
enumerará los métodos que se definen, suponiendo que toda la implementación de la clase vaya al archivo de objetos lógicos. Puede comparar eso con lo que se define como virtual para descubrir lo que se perdió.Por lo que vale, olvidar un cuerpo en un destructor virtual genera lo siguiente:
Estoy agregando una nota porque el mensaje de error es engañoso. (Esto fue con gcc versión 4.6.3.)
fuente
~Destructor = default;
en el archivo de encabezado no ayudó. ¿Hay un error documentado presentado contra gcc?Entonces, descubrí el problema y fue una combinación de mala lógica y no estar totalmente familiarizado con el mundo de automake / autotools. Estaba agregando los archivos correctos a mi plantilla Makefile.am, pero no estaba seguro de qué paso en nuestro proceso de compilación realmente creó el propio archivo MAKE. Entonces, estaba compilando un viejo archivo MAKE que no tenía idea de mis nuevos archivos.
Gracias por las respuestas y el enlace a las preguntas frecuentes de GCC. Me aseguraré de leer eso para evitar que este problema ocurra por una razón real.
fuente
Si está utilizando Qt, intente volver a ejecutar qmake. Si este error está en la clase del widget, qmake podría no haber notado que la clase ui vtable debería regenerarse. Esto solucionó el problema para mí.
fuente
qmake
, tuve lo mismo concmake
. Parte del problema podría ser que ambas herramientas tienen un pequeño problema con los archivos de encabezado, lo que no siempre desencadena una reconstrucción cuando es necesario.La referencia indefinida a vtable puede ocurrir debido a la siguiente situación también. Solo intenta esto:
La clase A contiene:
La clase B contiene:
La clase C contiene: ahora está escribiendo una clase C en la que la derivará de la clase A.
Ahora, si intenta compilar, obtendrá una referencia indefinida a vtable para la clase C como error.
Razón:
functionA
se define como virtual puro y su definición se proporciona en la Clase B.functionB
se define como virtual (NO VIRTUAL PURO) por lo que intenta encontrar su definición en la Clase A, pero usted proporcionó su definición en la Clase B.Solución:
virtual void functionB(parameters) =0;
(Esto funciona, se prueba)fuente
Simplemente recibí este error porque mi archivo cpp no estaba en el archivo MAKE.
fuente
undefined reference to {function/class/struct}
cuando hayvirtual
cosas involucradas. Me tiró¿Qué es un
vtable
?Puede ser útil saber de qué está hablando el mensaje de error antes de intentar solucionarlo. Comenzaré en un nivel alto, luego bajaré a algunos detalles más. De esa manera, las personas pueden saltar una vez que se sientan cómodos con su comprensión de las vtables. ... y hay un montón de gente saltando por delante en este momento. :) Para aquellos que se quedan:
Una vtable es básicamente la implementación más común del polimorfismo en C ++ . Cuando se usan vtables, cada clase polimórfica tiene una vtable en algún lugar del programa; Puedes considerarlo como un
static
miembro de datos (oculto) de la clase. Cada objeto de una clase polimórfica está asociado con la tabla vtable para su clase más derivada. Al marcar esta asociación, el programa puede hacer funcionar su magia polimórfica. Advertencia importante: una vtable es un detalle de implementación. No es un mandato del estándar C ++, aunque la mayoría (¿todos?) De los compiladores C ++ usan vtables para implementar el comportamiento polimórfico. Los detalles que presento son enfoques típicos o razonables. ¡Los compiladores pueden desviarse de esto!Cada objeto polimórfico tiene un puntero (oculto) a la tabla v para la clase más derivada del objeto (posiblemente múltiples punteros, en los casos más complejos). Al observar el puntero, el programa puede determinar cuál es el tipo "real" de un objeto (excepto durante la construcción, pero omita ese caso especial). Por ejemplo, si un objeto de tipo
A
no apunta a la tabla deA
, entonces ese objeto es en realidad un sub-objeto de algo derivado deA
.El nombre "vtable" viene de " v función irtual mesa ". Es una tabla que almacena punteros a funciones (virtuales). Un compilador elige su convención para la disposición de la tabla; un enfoque simple es pasar por las funciones virtuales en el orden en que se declaran dentro de las definiciones de clase. Cuando se llama a una función virtual, el programa sigue el puntero del objeto a una tabla v, va a la entrada asociada con la función deseada y luego usa el puntero de función almacenado para invocar la función correcta. Hay varios trucos para hacer que esto funcione, pero no los abordaré aquí.
¿Dónde / cuándo se
vtable
genera un ?El compilador genera automáticamente una vtable (a veces llamada "emitida"). Un compilador podría emitir una vtable en cada unidad de traducción que vea una definición de clase polimórfica, pero eso generalmente sería una exageración innecesaria. Una alternativa ( utilizada por gcc , y probablemente por otros) es elegir una sola unidad de traducción en la que colocar la vtable, similar a cómo elegiría un único archivo fuente en el que colocar los miembros de datos estáticos de una clase. Si este proceso de selección no puede seleccionar ninguna unidad de traducción, entonces la tabla vtable se convierte en una referencia indefinida. De ahí el error, cuyo mensaje no es particularmente claro.
Del mismo modo, si el proceso de selección elige una unidad de traducción, pero ese archivo de objeto no se proporciona al vinculador, entonces la tabla vtable se convierte en una referencia indefinida. Desafortunadamente, el mensaje de error puede ser aún menos claro en este caso que en el caso en que el proceso de selección falló. (Gracias a los que respondieron que mencionaron esta posibilidad. Probablemente lo habría olvidado de otra manera).
El proceso de selección utilizado por gcc tiene sentido si comenzamos con la tradición de dedicar un archivo fuente (único) a cada clase que necesita uno para su implementación. Sería bueno emitir el vtable al compilar ese archivo fuente. Llamemos a eso nuestro objetivo. Sin embargo, el proceso de selección debe funcionar incluso si no se sigue esta tradición. Entonces, en lugar de buscar la implementación de toda la clase, busquemos la implementación de un miembro específico de la clase. Si se sigue la tradición, y si ese miembro se implementa de hecho , esto logra el objetivo.
El miembro seleccionado por gcc (y potencialmente por otros compiladores) es la primera función virtual no en línea que no es puramente virtual. Si eres parte de la multitud que declara constructores y destructores antes que otras funciones miembro, entonces ese destructor tiene una buena posibilidad de ser seleccionado. (Se acordó de hacer que el destructor sea virtual, ¿verdad?) Hay excepciones; Esperaría que las excepciones más comunes sean cuando se proporciona una definición en línea para el destructor y cuando se solicita el destructor predeterminado (usando "
= default
").El astuto podría notar que una clase polimórfica puede proporcionar definiciones en línea para todas sus funciones virtuales. ¿Eso no hace que el proceso de selección falle? Lo hace en compiladores más antiguos. He leído que los últimos compiladores han abordado esta situación, pero no sé los números de versión relevantes. Podría intentar buscar esto, pero es más fácil codificarlo o esperar a que el compilador se queje.
En resumen, hay tres causas clave del error "referencia indefinida a vtable":
Estas causas son en sí mismas insuficientes para causar el error por sí mismas. Más bien, esto es lo que abordaría para resolver el error. No espere que crear intencionalmente una de estas situaciones produzca definitivamente este error; Hay otros requisitos. Espere que resolver estas situaciones resuelva este error.
(OK, el número 3 podría haber sido suficiente cuando se hizo esta pregunta).
¿Cómo arreglar el error?
¡Bienvenido de nuevo a la gente que salta por delante! :)
= 0
") y cuya definición proporcione (no "= default
").Ejemplo
Los detalles de qué hacer pueden variar y, a veces, ramificarse en preguntas separadas (como ¿Qué es una referencia indefinida / error de símbolo externo no resuelto y cómo lo soluciono? ). Sin embargo, proporcionaré un ejemplo de qué hacer en un caso específico que podría confundir a los programadores más nuevos.
El paso 1 menciona la modificación de su clase para que tenga una función de cierto tipo. Si la descripción de esa función se te pasó por alto, podrías estar en la situación que pretendo abordar. Tenga en cuenta que esta es una forma de lograr el objetivo; no es la única forma, y fácilmente podría haber mejores formas en su situación específica. Llamemos a tu clase
A
. ¿Su destructor está declarado (en su definición de clase) comoo
? Si es así, dos pasos cambiarán su destructor en el tipo de función que queremos. Primero, cambie esa línea a
Segundo, ponga la siguiente línea en un archivo fuente que sea parte de su proyecto (preferiblemente el archivo con la implementación de la clase, si tiene uno):
Eso hace que su destructor (virtual) no esté en línea y no sea generado por el compilador. (No dude en modificar las cosas para que coincidan mejor con su estilo de formato de código, como agregar un comentario de encabezado a la definición de la función).
fuente
Hay mucha especulación en varias respuestas aquí. A continuación daré un código bastante mínimo que reproduce este error y explicaré por qué está ocurriendo.
Código bastante mínimo para reproducir este error
IBase.hpp
Derived.hpp
Derived.cpp
myclass.cpp
Puede compilar esto usando GCC de esta manera:
Ahora puede reproducir el error eliminando
= 0
en IBase.hpp. Me sale este error:Explicación
Tenga en cuenta que el código anterior no requiere ningún destructor virtual, constructor o cualquier otro archivo adicional para que la compilación sea exitosa (aunque debería tenerlos).
La forma de entender este error es la siguiente: Linker está buscando el constructor de IBase. Este lo necesitará para el constructor de Derived. Sin embargo, como Derivado anula los métodos de IBase, tiene una tabla adjunta que hará referencia a IBase. Cuando el enlazador dice "referencia indefinida a vtable para IBase", básicamente significa que Derived tiene una referencia de vtable a IBase, pero no puede encontrar ningún código objeto compilado de IBase para admirar. Entonces, la conclusión es que la clase IBase tiene declaraciones sin implementaciones. Esto significa que un método en IBase se declara como virtual, pero olvidamos marcarlo como virtual puro O proporcionar su definición.
Consejo de despedida
Si todo lo demás falla, una forma de depurar este error es compilar un programa mínimo que compile y luego seguir cambiándolo para que llegue al estado que desea. En el medio, siga compilando para ver cuándo comienza a fallar.
Nota sobre el sistema de construcción ROS y Catkin
Si estaba compilando el conjunto de clases anterior en ROS utilizando el sistema de compilación catkin, necesitará las siguientes líneas en CMakeLists.txt:
La primera línea básicamente dice que queremos hacer un ejecutable llamado myclass y el código para construirlo se puede encontrar en los siguientes archivos. Uno de estos archivos debe tener main (). Tenga en cuenta que no tiene que especificar archivos .hpp en ninguna parte de CMakeLists.txt. Además, no tiene que especificar Derived.cpp como biblioteca.
fuente
Acabo de encontrar otra causa para este error que puedes verificar.
La clase base definió una función virtual pura como:
Y la subclase tenía
El problema era el error tipográfico que
"=0"
se suponía que debía estar fuera del paréntesis:Entonces, en caso de que te estés desplazando hacia abajo, probablemente no hayas encontrado la respuesta; esto es algo más para verificar.
fuente
Esto puede suceder fácilmente si olvida vincular al archivo de objeto que tiene la definición.
fuente
El compilador GNU C ++ tiene que tomar una decisión sobre dónde ubicarlo
vtable
en caso de que tenga la definición de las funciones virtuales de un objeto distribuidas en varias unidades de compilación (por ejemplo, algunas de las definiciones de funciones virtuales de los objetos están en un archivo .cpp y otras en otro. archivo cpp, y así sucesivamente).El compilador elige colocar el
vtable
en el mismo lugar donde se define la primera función virtual declarada.Ahora, si por alguna razón olvidó proporcionar una definición para la primera función virtual declarada en el objeto (u olvidó por error agregar el objeto compilado en la fase de enlace), obtendrá este error.
Como efecto secundario, tenga en cuenta que solo para esta función virtual en particular no obtendrá el error de enlazador tradicional como si le faltara la función foo .
fuente
No para cruzar el poste pero. Si se trata de herencia, el segundo hit de Google fue lo que me había perdido, es decir. Todos los métodos virtuales deben ser definidos.
Como:
Consulte Answare C ++ Referencia indefinida a vtable y herencia para más detalles. Acabo de darme cuenta de que ya se mencionó anteriormente, pero diablos podría ayudar a alguien.
fuente
Ok, la solución a esto es que es posible que se haya perdido la definición. Consulte el siguiente ejemplo para evitar el error del compilador de vtable:
fuente
CDasherComponent
tiene un cuerpo para el destructor? Definitivamente no está aquí; la pregunta es si está en el archivo .cc.CDasherModule
debe definir explícitamente su destructorvirtual
.CGameModule
tiene un extra}
al final (después del}; // for the class
).CGameModule
está vinculando contra las bibliotecas que definenCDasherModule
yCDasherComponent
?fuente
¿Quizás perder el destructor virtual es un factor contribuyente?
fuente
Este fue el primer resultado de búsqueda para mí, así que pensé en agregar otra cosa para verificar: asegurarme de que la definición de funciones virtuales esté realmente en la clase. En mi caso, tuve esto:
Archivo de cabecera:
y en mi archivo .cc:
Esto debería leer
fuente
Tal vez no. Definitivamente
~CDasherModule() {}
falta.fuente
Tantas respuestas aquí, pero ninguna de ellas parecía haber cubierto cuál era mi problema. Tuve lo siguiente:
Y en otro archivo (incluido en la compilación y vinculación, por supuesto)
Bueno, esto no funcionó y recibí el error del que todos hablan. Para resolverlo, tuve que mover la definición real de Foo fuera de la declaración de clase como tal:
No soy un gurú de C ++, así que no puedo explicar por qué esto es más correcto, pero resolvió el problema para mí.
fuente
Así que estaba usando Qt con Windows XP y el compilador MinGW y esto me estaba volviendo loco.
Básicamente, el moc_xxx.cpp se generó vacío incluso cuando me agregaron
Q_OBJECT
Eliminar todo lo que hace que las funciones sean virtuales, explícitas y cualquier cosa que adivines no funcionó. Finalmente comencé a eliminar línea por línea y resultó que tenía
Alrededor del archivo. Incluso cuando el #ifdef era verdadero, no se generó el archivo moc.
Entonces, eliminar todos los #ifdefs solucionó el problema.
Esto no sucedía con Windows y VS 2013.
fuente
g++ *.cpp ...
. (Necesitaba algo rápido y sucio, pero qmake estaba lleno de dolor.)Si todo lo demás falla, busque duplicación. La referencia inicial explícita a los constructores y destructores me dirigió mal hasta que leí una referencia en otra publicación. Es cualquier método no resuelto. En mi caso, pensé que había reemplazado la declaración que usaba char * xml como parámetro con una que usaba el const char * xml innecesariamente problemático, pero en su lugar, había creado una nueva y había dejado la otra en su lugar.
fuente
Se mencionan muchas posibilidades para causar este error, y estoy seguro de que muchas de ellas causan el error. En mi caso, había otra definición de la misma clase, debido a una duplicación del archivo fuente. Este archivo fue compilado, pero no vinculado, por lo que el vinculador se quejaba de no poder encontrarlo.
Para resumir, diría que si ha observado la clase lo suficiente y no puede ver qué posible problema de sintaxis podría estar causando, busque problemas de compilación como un archivo faltante o un archivo duplicado.
fuente
En mi caso, estoy usando Qt y había definido una
QObject
subclase en un archivofoo.cpp
(no.h
). La solución fue agregar#include "foo.moc"
al final defoo.cpp
.fuente
Creo que también vale la pena mencionar que también recibirá el mensaje cuando intente vincular al objeto de cualquier clase que tenga al menos un método virtual y el vinculador no puede encontrar el archivo. Por ejemplo:
Foo.hpp:
Foo.cpp:
Compilado con:
Y main.cpp:
Compilado y vinculado con:
Da nuestro error favorito:
Esto ocurre desde mi causa excepcional:
Vtable se crea por clase en tiempo de compilación
Linker no tiene acceso a vtable que está en Foo.o
fuente
Recibí este error en el siguiente escenario
Considere un caso en el que ha definido la implementación de funciones miembro de una clase en el archivo de encabezado. Este archivo de encabezado es un encabezado exportado (en otras palabras, podría copiarse a algún común / incluir directamente en su base de código). Ahora ha decidido separar la implementación de las funciones miembro al archivo .cpp. Después de separar / mover la implementación a .cpp, el archivo de encabezado ahora solo tiene los prototipos de las funciones miembro dentro de la clase. Después de los cambios anteriores, si construye su base de código puede recibir el error "referencia indefinida a 'vtable ...".
Para solucionar esto, antes de compilar, asegúrese de eliminar el archivo de encabezado (al que realizó los cambios) en el directorio common / include. También asegúrese de cambiar su archivo MAKE para acomodar / agregar el nuevo archivo .o creado a partir del nuevo archivo .cpp que acaba de crear. Cuando realice estos pasos, el compilador / enlazador ya no se quejará.
fuente
Obtuve este tipo de error en situaciones en las que intentaba vincularme a un objeto cuando recibí un error que impedía que el objeto se agregara al archivo.
Digamos que tengo libXYZ.a que se supone que tiene bioseq.o en int pero no lo tiene.
Recibí un error:
Esto es bastante diferente de todo lo anterior. Llamaría a este objeto faltante en el problema de archivo.
fuente
También es posible que recibas un mensaje como
si olvida definir una función virtual de una clase FakeClass1 cuando intenta vincular una prueba unitaria para otra clase SomeClass.
Y
En este caso, le sugiero que revise su falso para la clase 1 una vez más. Probablemente descubrirá que puede haber olvidado definir una función virtual
ForgottenFunc
en su clase falsa.fuente
Recibí este error cuando agregué una segunda clase a un par fuente / encabezado existente. Dos encabezados de clase en el mismo archivo .h, y definiciones de función para dos clases en el mismo archivo .cpp.
He hecho esto con éxito antes, con clases destinadas a trabajar juntas, pero aparentemente esta vez no me gustó. Todavía no sé qué, pero dividirlos en una clase por unidad de compilación lo arregló de inmediato.
El intento fallido:
_gui_icondata.h:
_gui_icondata.cpp:
Nuevamente, agregar un nuevo par fuente / encabezado y cortar / pegar la clase IconWithData textualmente "simplemente funcionó".
fuente
Mi caso fue tonto, tuve un extra
"
después#include
por error y ¿adivina qué?He estado rascándome la cabeza y la cara palmeando durante horas comentando funciones virtuales para ver si algo cambió, y finalmente al eliminar el extra
"
, ¡todo se solucionó! Este tipo de cosas realmente necesitan dar como resultado un error de compilación, no un error de enlace.Por extra
"
, quiero decir:fuente
En mi caso, tenía una clase base llamada Persona y dos clases derivadas llamadas Estudiante y Profesor.
Cómo se reparó mi programa fue: 1. Hice todas las funciones en la clase base
Pure Virtual.
2. Usé todos los destructores virtuales comodefault ones.
fuente
Recibí este error solo porque el nombre de un argumento de constructor difería en el archivo de encabezado y en el archivo de implementación. La firma del constructor es
y lo que escribí en la implementación comenzó con
así accidentalmente reemplacé "pset" por "plaga". El compilador se quejaba de este y otros dos constructores en los que no había ningún error. Estoy usando g ++ versión 4.9.1 en Ubuntu. Y la definición de un destructor virtual en esta clase derivada no hizo ninguna diferencia (se define en la clase base). Nunca habría encontrado este error si no hubiera pegado los cuerpos de los constructores en el archivo de encabezado, definiéndolos así en clase.
fuente