Ao criar meu programa C ++, estou recebendo a mensagem de erro
referência indefinida a 'vtable ...
Qual é a causa desse problema? Como faço para corrigir isso?
Acontece que estou recebendo o erro do código a seguir (a classe em questão é CGameModule.) E durante toda a minha vida não consigo entender qual é o problema. No começo, pensei que estava relacionado ao esquecimento de atribuir um corpo a uma função virtual, mas, pelo que entendi, tudo está aqui. A cadeia de herança é um pouco longa, mas aqui está o código-fonte relacionado. Não tenho certeza de quais outras informações devo fornecer.
Nota: Parece que o construtor está onde este erro está acontecendo.
Meu 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;
};
Herda 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 herda 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
e depoisqmake
) para gerar um novoMakefile
, essa é uma provável fonte do erro ao usar o Qt.Q_OBJECT
é copiado externamente, mas ainda não faz parte do arquivo .pro, embora seja compilado corretamente, ele não vincula. Temos que adicionar esse.h/.cpp
arquivo no arquivo .pro para podermosqmake
.Respostas:
As Perguntas frequentes do GCC têm uma entrada:
fonte
nm -C CGameModule.o | grep CGameModule::
listará os métodos definidos, assumindo que toda a implementação da classe vá para o arquivo de objeto lógico. Você pode comparar isso com o que é definido como virtual para descobrir o que você perdeu.Pelo que vale a pena, esquecer um corpo em um destruidor virtual gera o seguinte:
Estou adicionando uma observação porque a mensagem de erro é enganosa. (Isso ocorreu com a versão 4.6.3 do gcc.)
fonte
~Destructor = default;
no arquivo de cabeçalho não ajudou. Existe um bug documentado arquivado no gcc?Então, eu descobri o problema e era uma combinação de lógica ruim e não estar totalmente familiarizado com o mundo das ferramentas automáticas / automáticas. Eu estava adicionando os arquivos corretos ao meu modelo Makefile.am, mas não tinha certeza de qual etapa do nosso processo de criação realmente criou o próprio makefile. Então, eu estava compilando com um makefile antigo que não fazia ideia dos meus novos arquivos.
Obrigado pelas respostas e pelo link para as Perguntas frequentes do GCC. Certificarei-me de ler isso para evitar que esse problema ocorra por um motivo real.
fonte
Se você estiver usando o Qt, tente executar novamente o qmake. Se esse erro estiver na classe do widget, qmake pode ter falhado em perceber que a tabela de classe da interface do usuário deve ser regenerada. Isso corrigiu o problema para mim.
fonte
qmake
, eu tive o mesmo comcmake
. Parte do problema pode ser que ambas as ferramentas têm um problema com os arquivos de cabeçalho, que nem sempre acionam uma reconstrução quando necessário.Referências indefinidas à vtable também podem ocorrer devido à seguinte situação. Apenas tente o seguinte:
Classe A Contém:
Classe B Contém:
A classe C contém: Agora você está escrevendo uma classe C na qual a derivará da classe A.
Agora, se você tentar compilar, obterá uma referência indefinida ao vtable para a classe C como erro.
Razão:
functionA
é definido como virtual puro e sua definição é fornecida na Classe B.functionB
é definida como virtual (NÃO PURE VIRTUAL); portanto, tenta encontrar sua definição na própria Classe A, mas você forneceu sua definição na Classe B.Solução:
virtual void functionB(parameters) =0;
(isso funciona, é testado)fonte
Eu simplesmente recebi esse erro porque meu arquivo cpp não estava no makefile.
fonte
undefined reference to {function/class/struct}
quando hávirtual
coisas envolvidas. Me jogou fora.O que é um
vtable
?Pode ser útil saber do que a mensagem de erro está falando antes de tentar corrigi-la. Vou começar em um nível alto e depois trabalhar com mais alguns detalhes. Dessa forma, as pessoas podem pular adiante assim que se sentirem confortáveis com o entendimento das tabelas vt. ... e lá vai um monte de pessoas pulando adiante agora. :) Para aqueles que ficam por aqui:
Uma vtable é basicamente a implementação mais comum de polimorfismo em C ++ . Quando vtables são usadas, toda classe polimórfica tem uma tabela em algum lugar do programa; você pode pensar nisso como um
static
membro de dados (oculto) da classe. Todo objeto de uma classe polimórfica é associado ao vtable para sua classe mais derivada. Ao verificar essa associação, o programa pode trabalhar sua mágica polimórfica. Advertência importante: uma vtable é um detalhe de implementação. Não é obrigatório pelo padrão C ++, embora a maioria dos compiladores C ++ (todos?) Use vtables para implementar comportamento polimórfico. Os detalhes que estou apresentando são abordagens típicas ou razoáveis. Compiladores podem se desviar disso!Cada objeto polimórfico possui um ponteiro (oculto) para a vtable para a classe mais derivada do objeto (possivelmente vários ponteiros, nos casos mais complexos). Observando o ponteiro, o programa pode dizer qual é o tipo "real" de um objeto (exceto durante a construção, mas vamos pular esse caso especial). Por exemplo, se um objeto do tipo
A
não apontar para a vtable deA
, então esse objeto é realmente um subobjeto de algo derivadoA
.O nome "vtable" vem de " v irtual function table ". É uma tabela que armazena ponteiros para funções (virtuais). Um compilador escolhe sua convenção de como a tabela é organizada; Uma abordagem simples é analisar as funções virtuais na ordem em que são declaradas nas definições de classe. Quando uma função virtual é chamada, o programa segue o ponteiro do objeto até uma tabela, vai para a entrada associada à função desejada e usa o ponteiro da função armazenada para chamar a função correta. Existem vários truques para fazer isso funcionar, mas não vou abordar esses aqui.
Onde / quando é
vtable
gerado?Uma vtable é gerada automaticamente (às vezes chamada de "emitida") pelo compilador. Um compilador pode emitir uma vtable em todas as unidades de tradução que veem uma definição de classe polimórfica, mas isso geralmente seria um exagero desnecessário. Uma alternativa ( usada pelo gcc e provavelmente por outros) é escolher uma única unidade de tradução na qual colocar a vtable, semelhante a como você escolheria um único arquivo de origem para colocar os membros de dados estáticos de uma classe. Se esse processo de seleção falhar na escolha de quaisquer unidades de tradução, a tabela v se torna uma referência indefinida. Daí o erro, cuja mensagem não é reconhecidamente clara.
Da mesma forma, se o processo de seleção escolher uma unidade de tradução, mas esse arquivo de objeto não for fornecido ao vinculador, a vtable se tornará uma referência indefinida. Infelizmente, a mensagem de erro pode ser ainda menos clara nesse caso do que no caso em que o processo de seleção falhou. (Obrigado aos respondentes que mencionaram essa possibilidade. Eu provavelmente teria esquecido o contrário.)
O processo de seleção usado pelo gcc faz sentido se começarmos com a tradição de dedicar um arquivo de origem (único) a cada classe que precisa de um para sua implementação. Seria bom emitir a vtable ao compilar esse arquivo de origem. Vamos chamar isso de nosso objetivo. No entanto, o processo de seleção precisa funcionar, mesmo que essa tradição não seja seguida. Portanto, em vez de procurar a implementação de toda a classe, vamos procurar a implementação de um membro específico da classe. Se a tradição for seguida - e se esse membro for de fato implementado -, isso alcançará o objetivo.
O membro selecionado pelo gcc (e potencialmente por outros compiladores) é a primeira função virtual não embutida que não é virtual pura. Se você faz parte da multidão que declara construtores e destruidores antes de outras funções-membro, esse destruidor tem uma boa chance de ser selecionado. (Você se lembrou de tornar o destruidor virtual, certo?) Há exceções; Eu esperaria que as exceções mais comuns sejam quando uma definição em linha é fornecida para o destruidor e quando o destruidor padrão é solicitado (usando "
= default
").O astuto pode perceber que uma classe polimórfica pode fornecer definições em linha para todas as suas funções virtuais. Isso não causa falha no processo de seleção? Isso acontece em compiladores mais antigos. Eu li que os compiladores mais recentes abordaram essa situação, mas não sei os números de versão relevantes. Eu poderia tentar procurar isso, mas é mais fácil codificá-lo ou aguardar a reclamação do compilador.
Em resumo, há três causas principais do erro "referência indefinida à vtable":
Essas causas são, por si só, insuficientes para causar o erro por conta própria. Em vez disso, é isso que você abordaria para resolver o erro. Não espere que a criação intencional de uma dessas situações definitivamente produza esse erro; existem outros requisitos. Espere que a solução dessas situações resolva esse erro.
(OK, o número 3 pode ter sido suficiente quando essa pergunta foi feita.)
Como corrigir o erro?
Bem-vindo de volta, pessoas pulando à frente! :)
= 0
") e cuja definição você fornece (não "= default
").Exemplo
Os detalhes do que fazer podem variar e às vezes se ramificam em perguntas separadas (como O que é uma referência indefinida / erro de símbolo externo não resolvido e como faço para corrigi-lo? ). Entretanto, fornecerei um exemplo do que fazer em um caso específico que pode atrapalhar os programadores mais novos.
A Etapa 1 menciona a modificação de sua classe para que ela tenha uma função de um determinado tipo. Se a descrição dessa função passou por sua cabeça, você pode estar na situação que pretendo abordar. Lembre-se de que essa é uma maneira de atingir a meta; não é o único caminho, e facilmente poderia haver caminhos melhores em sua situação específica. Vamos ligar para a sua turma
A
. Seu destruidor é declarado (na sua definição de classe) comoou
? Nesse caso, duas etapas mudarão seu destruidor para o tipo de função que queremos. Primeiro, mude essa linha para
Segundo, coloque a seguinte linha em um arquivo de origem que faça parte do seu projeto (de preferência o arquivo com a implementação da classe, se você tiver um):
Isso faz com que seu destruidor (virtual) não esteja em linha e não seja gerado pelo compilador. (Sinta-se à vontade para modificar as coisas para corresponder melhor ao seu estilo de formatação de código, como adicionar um comentário de cabeçalho à definição da função.)
fonte
Há muita especulação acontecendo em várias respostas aqui. Abaixo, darei um código mínimo que reproduz esse erro e explico por que ele está ocorrendo.
Código razoavelmente mínimo para reproduzir este erro
IBase.hpp
Derived.hpp
Derived.cpp
myclass.cpp
Você pode compilar isso usando o GCC assim:
Agora você pode reproduzir o erro removendo o
= 0
IBase.hpp. Eu recebo este erro:Explicação
Observe que o código acima não requer destruidores virtuais, construtores ou outros arquivos extras para que a compilação seja bem-sucedida (embora você deva tê-los).
A maneira de entender esse erro é a seguinte: O Linker está procurando pelo construtor do IBase. Isso será necessário para o construtor de Derivado. No entanto, como Derivado substitui os métodos do IBase, ele tem uma tabela anexável que fará referência ao IBase. Quando o vinculador diz "referência indefinida à vtable para IBase", significa basicamente que o Derived possui uma referência vtable à IBase, mas não consegue encontrar nenhum código de objeto compilado do IBase para procurar. Portanto, o ponto principal é que a classe IBase possui declarações sem implementações. Isso significa que um método no IBase é declarado como virtual, mas esquecemos de marcá-lo como virtual puro OU fornecer sua definição.
Dica de despedida
Se tudo mais falhar, uma maneira de depurar esse erro é criar um programa mínimo que seja compilado e depois alterá-lo para que ele chegue ao estado desejado. No meio, continue compilando para ver quando começa a falhar.
Nota sobre o sistema de construção ROS e Catkin
Se você estava compilando acima do conjunto de classes no ROS usando o sistema catkin build, precisará das seguintes linhas em CMakeLists.txt:
A primeira linha basicamente diz que queremos criar um executável chamado myclass e o código para compilar isso pode ser encontrado nos arquivos a seguir. Um desses arquivos deve ter main (). Observe que você não precisa especificar arquivos .hpp em nenhum lugar do CMakeLists.txt. Além disso, você não precisa especificar Derived.cpp como biblioteca.
fonte
Acabei de encontrar outra causa para esse erro que você pode verificar.
A classe base definiu uma função virtual pura como:
E a subclasse tinha
O problema foi o erro de digitação que
"=0"
deveria estar fora dos parênteses:Portanto, caso você esteja rolando até aqui, você provavelmente não encontrou a resposta - isso é outra coisa para verificar.
fonte
Isso pode acontecer com bastante facilidade se você esquecer de vincular ao arquivo de objeto que possui a definição.
fonte
O compilador GNU C ++ precisa decidir onde colocar o
vtable
caso de você ter a definição das funções virtuais de um objeto espalhada por várias unidades de compilação (por exemplo, algumas das definições de funções virtuais dos objetos estão em um arquivo .cpp e outras em outro. arquivo cpp e assim por diante).O compilador escolhe colocar o
vtable
mesmo local onde a primeira função virtual declarada é definida.Agora, por algum motivo, você se esqueceu de fornecer uma definição para a primeira função virtual declarada no objeto (ou se esqueceu de adicionar o objeto compilado na fase de vinculação por engano), receberá esse erro.
Como efeito colateral, observe que somente para esta função virtual específica você não receberá o erro tradicional do vinculador como se estivesse faltando a função foo .
fonte
Não para atravessar post, mas. Se você está lidando com herança, o segundo hit do Google foi o que eu tinha perdido, ou seja. todos os métodos virtuais devem ser definidos.
Tal como:
Consulte a Referência Indefinida C ++ da answare para vtable e herança para obter detalhes. Acabei de perceber que já foi mencionado acima, mas que diabos isso pode ajudar alguém.
fonte
Ok, a solução para isso é que você pode ter perdido a definição. Veja o exemplo abaixo para evitar o erro do compilador vtable:
fonte
CDasherComponent
possui um corpo para o destruidor? Definitivamente não está aqui - a questão é se está no arquivo .cc.CDasherModule
deve definir explicitamente seu destruidorvirtual
.CGameModule
tem um extra}
no final (depois do}; // for the class
).CGameModule
sendo vinculado às bibliotecas que definemCDasherModule
eCDasherComponent
?fonte
Talvez a falta do destruidor virtual esteja contribuindo com o fator?
fonte
Este foi o primeiro resultado de pesquisa para mim, então pensei em adicionar outra coisa para verificar: verifique se a definição de funções virtuais está realmente na classe. No meu caso, eu tive o seguinte:
Arquivo de cabeçalho:
e no meu arquivo .cc:
Isso deve ler
fonte
Talvez não. Definitivamente
~CDasherModule() {}
está faltando.fonte
Tantas respostas aqui, mas nenhuma delas parecia ter coberto o meu problema. Eu tive o seguinte:
E em outro arquivo (incluído na compilação e vinculação, é claro)
Bem, isso não funcionou e eu recebi o erro que todo mundo está falando. Para resolvê-lo, tive que mover a definição real de Foo da declaração de classe como tal:
Como não sou um guru de C ++, não posso explicar por que isso é mais correto, mas resolveu o problema para mim.
fonte
Então, eu estava usando o Qt com o Windows XP e o compilador MinGW e isso estava me deixando louco.
Basicamente, o moc_xxx.cpp foi gerado vazio, mesmo quando fui adicionado
Q_OBJECT
Excluir tudo que torna as funções virtuais, explícitas e o que você acha que não funciona. Por fim, comecei a remover linha por linha e verificou-se que eu tinha
Ao redor do arquivo. Mesmo quando o #ifdef era verdadeiro, o arquivo moc não foi gerado.
A remoção de todos os #ifdefs corrigiu o problema.
Isso não estava acontecendo com o Windows e o VS 2013.
fonte
g++ *.cpp ...
. (Necessário rápida algo e sujo, mas qmake estava cheio de tristeza.)Se tudo mais falhar, procure duplicação. Fui mal direcionado pela referência inicial explícita a construtores e destruidores até ler uma referência em outro post. É qualquer método não resolvido. No meu caso, pensei que havia substituído a declaração que usava char * xml como parâmetro por uma que usava o const char * xml desnecessariamente problemático, mas, em vez disso, criei uma nova e deixei a outra no lugar.
fonte
Existem muitas possibilidades mencionadas para causar esse erro, e tenho certeza que muitas delas causam o erro. No meu caso, havia outra definição da mesma classe, devido a uma duplicação do arquivo de origem. Esse arquivo foi compilado, mas não vinculado, portanto, o vinculador estava reclamando por não conseguir encontrá-lo.
Para resumir, eu diria que, se você ficou olhando a classe por tempo suficiente e não consegue ver o possível problema de sintaxe que pode estar causando, procure problemas de compilação, como um arquivo ausente ou um arquivo duplicado.
fonte
No meu caso, estou usando o Qt e defini uma
QObject
subclasse em um arquivofoo.cpp
(não.h
). A correção foi adicionada#include "foo.moc"
no final defoo.cpp
.fonte
Também acho que vale a pena mencionar que você também receberá a mensagem ao tentar vincular ao objeto de qualquer classe que tenha pelo menos um método virtual e o vinculador não pode encontrar o arquivo. Por exemplo:
Foo.hpp:
Foo.cpp:
Compilado com:
E main.cpp:
Compilado e vinculado a:
Dá o nosso erro favorito:
Esta ocorrência do meu entendimento, porque:
O Vtable é criado por classe em tempo de compilação
O vinculador não tem acesso ao vtable que está no Foo.o
fonte
Eu recebi esse erro no seguinte cenário
Considere um caso em que você definiu a implementação das funções de membro de uma classe no próprio arquivo de cabeçalho. Este arquivo de cabeçalho é um cabeçalho exportado (em outras palavras, pode ser copiado para alguns arquivos comuns / incluídos diretamente na sua base de código). Agora você decidiu separar a implementação das funções de membro para o arquivo .cpp. Depois que você separou / moveu a implementação para .cpp, o arquivo de cabeçalho agora possui apenas os protótipos das funções de membro dentro da classe. Após as alterações acima, se você criar sua base de código, poderá receber o erro "referência indefinida para 'vtable ...".
Para corrigir isso, antes da compilação, exclua o arquivo de cabeçalho (no qual você fez alterações) no diretório common / include. Também certifique-se de alterar seu makefile para acomodar / adicionar o novo arquivo .o criado a partir do novo arquivo .cpp que você acabou de criar. Quando você executa essas etapas, o compilador / vinculador não reclama mais.
fonte
Eu recebi esse tipo de erro nas situações em que estava tentando vincular a um objeto quando recebi um bug que impedia que o objeto fosse adicionado ao arquivo morto.
Digamos que eu tenho libXYZ.a que deveria ter bioseq.o em int, mas não possui.
Eu recebi um erro:
Isso é diferente de todos os itens acima. Eu chamaria esse objeto ausente no problema de arquivamento.
fonte
Também é possível que você receba uma mensagem como
se você esquecer de definir uma função virtual de uma classe FakeClass1 ao tentar vincular um teste de unidade a outra classe SomeClass.
E
Nesse caso, sugiro que você verifique seu falso para class1 mais uma vez. Você provavelmente descobrirá que esqueceu de definir uma função virtual
ForgottenFunc
em sua classe falsa.fonte
Eu recebi esse erro quando adicionei uma segunda classe a um par de fonte / cabeçalho existente. Dois cabeçalhos de classe no mesmo arquivo .he definições de função para duas classes no mesmo arquivo .cpp.
Eu já fiz isso com sucesso antes, com aulas destinadas a trabalhar em conjunto, mas aparentemente algo não gostou de mim neste momento. Ainda não sei o que, mas dividi-los em uma classe por unidade de compilação corrigiu tudo.
A tentativa falhada:
_gui_icondata.h:
_gui_icondata.cpp:
Novamente, adicionar um novo par de fonte / cabeçalho e cortar / colar a classe IconWithData literalmente "simplesmente funcionou".
fonte
Meu caso foi bobo, tive um extra
"
depois#include
por engano e adivinhem?Estive coçando a cabeça e o rosto por horas comentando funções virtuais para ver se alguma coisa mudou e, finalmente, removendo o extra
"
, tudo foi consertado! Esse tipo de coisa realmente precisa resultar em erro de compilação e não erro de link.Por extra
"
, quero dizer:fonte
No meu caso, eu tinha uma classe base chamada Pessoa e duas classes derivadas chamadas Estudante e Professor.
Como meu programa foi corrigido foi: 1. Fiz todas as funções na classe base
Pure Virtual.
2. Usei todos os destruidores virtuais comodefault ones.
fonte
Eu recebi esse erro apenas porque o nome de um argumento do construtor diferia no arquivo de cabeçalho e no arquivo de implementação. A assinatura do construtor é
e o que eu escrevi na implementação começou com
assim, acidentalmente substituí "pset" por "pest". O compilador estava reclamando deste e de outros dois construtores nos quais não havia nenhum erro. Estou usando o g ++ versão 4.9.1 no Ubuntu. E definir um destruidor virtual nessa classe derivada não fez diferença (é definido na classe base). Eu nunca teria encontrado esse bug se não colasse os corpos dos construtores no arquivo de cabeçalho, definindo-os na classe.
fonte