Неопределенная ссылка на «vtable для xxx»

takeaway.o: In function `takeaway':
project:145: undefined reference to `vtable for takeaway'
project:145: undefined reference to `vtable for takeaway'
takeaway.o: In function `~takeaway':
project:151: undefined reference to `vtable for takeaway'
project:151: undefined reference to `vtable for takeaway'
takeaway.o: In function `gameCore':
project.h:109: undefined reference to `gameCore<int>::initialData(int)'
collect2: ld returned 1 exit status
make: *** [takeaway] Error 1

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

У меня есть шаблонный класс (gameCore.h) и класс (takeaway.cpp), который наследуется от gameCore. Ошибка vtable вызывается 3 раза 1) в конструкторе выносов 2) в деструкторе выносов 3) в конструкторе gameCores

Я использую G++ Вот код: (я знаю, что это может показаться трудным для чтения, но я точно отметил, где возникают ошибки) takeaway.h

#ifndef _TAKEAWAY_H_
#define _TAKEAWAY_H_
#include<map>
#include<cctype>
#include<stack>
#include<map>
#include<iostream>
#include<string>
#include<cstdlib>
#include"gameCore.h"
#include<vector>
using namespace std;
class takeaway : public gameCore<int>
{
 private:

 public:
// template<class Penny>
 void  textualGame();
 bool isNum(string str);
// template<class Penny>
 stack<int> initialData(int initial);
// template<class Position>
 int score (int position);
// template<class Position>
 stack<int> addStack(int currentPos, stack<int> possiblePositions);
// template<class Penny>
 takeaway (int initial);
// template<class Position>
 ~takeaway();
};
bool isNum(string str);
int charToint(char *theChar);
#endif

еда на вынос.cpp

/*
Description :
    This game communicates with the gameCore class to determine the results
    of a game of takeaway played between two computers or a computer and human.   
*/

#include "takeaway.h"

 /*
 Description:Creates a stack represening initial data
 Note:Change to a vector eventually
 return : stack of int
 */
 stack<int> takeaway:: initialData(int initial){
   stack<int> returnStack;
   int theScore = score(initial);
   int final;
   if(initial ==0)
   {
    final = 1;
   }
   else
   {
    final = 0;
   }
   returnStack.push(theScore);
   returnStack.push(final);
   return returnStack;
 }


 /*
 Description: a textual representation of the game
 Note: This is still terribly wrong
 */

 void textualGame(){
  cout <<"this is the best i could do for a graphical representation";

 }
 /*
 Description: Deetermines if a number is even
 Note: Helper function for determining win or loss positions
 Returns: 1 if it is and 0 if it is not
 */
 int takeaway::score(int position){
  if(position % 2 == 0)
  {
     return 1;
  }
  return 0;
 }
 /*
   Description: Will return a stack , withouth the given postion in it
   will contain all positions possible after the given position
   along with anyother that wehre in the given stack.This function
   Must also update the map to represent updated positions
   Takes: a position to check and a stack to return
   Returns: A stack of possible positions.

 */
 stack<int>  takeaway::addStack(int currentPos, stack<int> possiblePositions ){
  if(currentPos != 0)
  {
    // If even
    if( currentPos % 2 == 0)
    { 
       // Create a data aray with score of the new positon and mark it as not final
    int data[] = {score(currentPos/2),0};
    vector<int> theData(data, data+sizeof(data));
        int pos = currentPos/2;
       // Add it to the map
       //this -> gamesMap[currentPos/2] = dataArray; 
       this -> gamesMap.insert(std::pair<int, vector<int> >(pos, theData));
       // Add it to the possible positions
       possiblePositions.push(pos);
    }
    if(currentPos % 3 == 0)
    {

    int data[] = {score(currentPos/3),0};
       vector<int> theData(data,data+sizeof(data));
       int  pos = currentPos/3;
       //this -> gamesMap[currentPos/3] = dataArray; 
       this -> gamesMap.insert(std::pair<int, vector<int> >(pos, theData));
       possiblePositions.push(pos);
    }
    // Work for the position that represents taking one penny
    int minusFinal = 0;
    if(currentPos - 1 == 0)
    {
      minusFinal = 1;
    }
    int data[] = {score(currentPos - 1),minusFinal};
    vector<int> theData(data,data+sizeof(data));
    int pos  = currentPos - 1;
   // this -> gamesMap[currentPos -1] = dataArary
    this->gamesMap.insert(std::pair<int,vector<int> >(pos, theData));
    possiblePositions.push(pos);
  }
  return possiblePositions;

 }
 /*
 Description: Constructor for the takeaway game
OA takes: a initial position, and initial data for it

 */
 takeaway::takeaway(int initial):gameCore<int>::gameCore(initial){ //<--- ERROR HERE
 //Constructor
 }
 /*
 Description: Destuctor
 */
 takeaway::~takeaway(){ // <--------------------- ERROR HERE
 //Destructor
 }


//checks input and creates game.
int main(int argc, char* argv[]){
  int numberPennies ;
  string game = argv[0];
  if(argc == 2 && isNum(argv[1]) )
  {
    int pennies = charToint(argv[1]);
     takeaway gameInstance(pennies ); // Creates a instance of $
  }
 //  else if(argc == 3 && argv[1] == "play" && isNum(argv[2]) )
 // {
 //   int pennies = charToint(argv[2]);
 //   takeaway<int> gameInstance(pennies); // Craete a human playab$
 // }
  else
  {
    cerr << "Error->Usage: " << game <<" [play] numberOfPennies \n";
    exit (1);
  }
 return 0;
 }

//Converts a char to a integer
int charToint(char *theChar){
  int theInt = atoi(theChar);
  return theInt;
}
 //Determines if a string is numeric
bool isNum(string str){ 
  for(int i = 0;i < str.length() ;i++){
   if(isdigit(str[i]) != 1)
   {
     cerr << "Error->Input: Number must be a Positive Integer the charecter '" << str[i]<< "' invalidated your input. \n" ;
     exit(1);
     return false;
   }
  }
  return true;
}

gameCore.h

/*
gameCore.h

Description:
    This class created gameMap that are written as a template
    They will communicate with the specific game and the algorithm
    To keep track of positions ans there values.
*/
#ifndef GAMECORE_H
#define GAMECORE_H
#include <map>
#include <stack>
#include <string>
#include <vector>
using namespace std;


template <class Position>
class gameCore
{
 protected:
    //Best Move used by algorithim
    Position bestMove;
    //The current highest score used by the algorithim
    int highestScore ;
    //Stack to be used to remmeber what move created the score
    stack<Position> movedFrom;
    //Stack used for the algorithim.
    stack<Position> curWorkingPos;
    //The actual Map that the data will be held in.
    map<Position,vector<int> > gamesMap;
 public:

    /*
    Description : finds the data array for a poisition
    takes: a Position
    Returns: a array of integers /**
    */
    virtual stack<int> initialData(Position pos) = 0;
        /*
    Description: Game must implement a way to determine a positions
    score.

    */
        virtual int score(Position pos) = 0;
        /*
    Description: A Graphical representation of the game

    */
    virtual void textualGame() = 0;

    /*
    Description: a virtual function implemented by the child class
    it will return a stack without the given position in it.This stack
    will contain all positions available from the given postion as well as 
    all position already in the given stack. Also it will update the map with
    all generated positions.
    TAkes: a postion to check and a stack of currently working positons.

    */
    virtual stack<Position> addStack(Position currentPos, stack<Position> possiblePositions ) = 0;
    /*
       Description:Constructor that
       Creates a Map with positions as the key.
       And an array of two integers that represent the positions
       value and if we have moved here in the past.
       Takes: a Initial Position and a Array of integers
    */
    gameCore(Position initial){              // <-----ERROR HERE
       //Determine the initial data and add it to the map and queue.
       stack<int> theData = initialData(initial);
       int first = theData.top();
           theData.pop();
           int second = theData.top();
       theData.pop();
       int initialData[] = {first,second};
           vector<int> posData(initialData,initialData+sizeof(initialData));
       gamesMap[initial] = posData;
       curWorkingPos.push(initial);
    }
    /*
    Description:
       A destructor for the class
    */
     ~gameCore(){
        //I do nothing but , this class needs a destructor

    }
    /*
       Description: Takes the current position and returns 
       that positions Score.
       Takes: A position 
       Returns:A integer that is a positions score.

    */
    int getPosScore(Position thePos) const {
        return this ->gamesMap.find(thePos)->second[0];
    }
    /*
    Description: Adds values to a stack based on the current position
    Takes: a poistion
    */
    void updateStack(Position curPos){
        this ->curWorkingPos =addStack(curPos,this ->curWorkingPos ); // get a stack from the game
        // The game has a function that takes a position and a stack and based on the positions returns a stack identical to the last but with added values that represent valid moves from the postion./
    }
    /*
       Description : Takes a positions and returns a integer
       that depends on if the position is a final pos or not
       Takes: A position
       Returns: A Bool that represents if the position is a final(1)  or not (0).

    */
        // Possible change
    bool isFinal(Position thePos) {     
        typename map<Position,vector<int> >::iterator iter =  this ->gamesMap.find(thePos);
        return iter->second[1] == 1 ;
    }
    /*
    Description: Based on the given position determine if a move needs to be made.
    (if not this is a end game position and it will return itself) If a move needs
    to be made it will return the position to move to that is ideal.
    Note: (because all positions can be represented as integers for any game , the return
    type is a integer)

    */
    int evaluatePosition(Position possiblePosition ){
           if(isFinal(possiblePosition)) //If this is a final position
        {
           return  getPosScore(possiblePosition);  //Return the score 
        }
           else
           {
         updateStack(possiblePosition); //Put all possible positions from this in thte stack
         while(this -> curWorkingPos.size() != 0)
         {
           this -> movedFrom.push(this->curWorkingPos.front()); //take the top of the possible positions stack and set it the the moved from stack
           this -> curWorkingPos.pop();
           int curScore =  evaluatePosition(this ->movedFrom.top());  //Recursive call for school
           curScore = curScore * -1; //Negate the score
           if(curScore > this -> highestScore) // if the score resulting from this position is biggest seen
           {
             highestScore = curScore;
             this ->movedFrom.pop();  //do this first to get rid of the the lowest point
             this -> bestMove = this ->movedFrom.top();  // mark where the lowest point came from
           }
          else
           {
             this -> movedFrom.pop(); 
           }
         }
           }
        return this -> bestMove;
    }
    //A Structure to determine if a position has a lower value than the second
    struct posCompare{
        bool operator() (Position pos1,Position pos2) const {
            return (pos1.getPosScore() < pos2.getPosScore());
            }
        };
};
#endif

person TacticalMin    schedule 05.10.2011    source источник
comment
Вам нужно опубликовать пример, а не просто извергать весь свой код.   -  person Puppy    schedule 05.10.2011
comment
Это хороший вопрос, но слишком много кода, попробуйте немного сузить его, чтобы мы могли легко понять, что происходит и в чем проблема.   -  person Grammin    schedule 05.10.2011
comment
Можете ли вы попытаться сократить свой код до небольшого примера, воспроизводящего проблему? Это поможет людям помочь вам. И это может даже помочь вам понять это самостоятельно. И, э-э, здесь действительно нужны комментарии к истории изменений?   -  person R. Martinho Fernandes    schedule 05.10.2011
comment
Я просто хотел бы добавить, что иногда эта ошибка возникает, когда деструктор производного класса не был определен.   -  person Nehal J Wani    schedule 07.04.2013
comment
Боже, как у этого вопроса может быть так много голосов. Вероятно, пользователям было бы лучше, если бы был только журнал ошибок, а лучшим ответом было бы первое предложение ответа @bdonlan.   -  person Jendas    schedule 02.03.2015
comment
Возможный дубликат неопределенной ссылки на vtable   -  person jww    schedule 01.06.2017


Ответы (7)


Первый набор ошибок для отсутствующей виртуальной таблицы вызван тем, что вы не реализуете takeaway::textualGame(); вместо этого вы реализуете функцию, не являющуюся членом, textualGame(). Я думаю, что добавление отсутствующего takeaway:: исправит это.

Причина последней ошибки в том, что вы вызываете виртуальную функцию initialData() из конструктора gameCore. На этом этапе виртуальные функции отправляются в соответствии с создаваемым в данный момент типом (gameCore), не наиболее производным классом (takeaway). Эта конкретная функция чисто виртуальная, поэтому вызов ее здесь дает неопределенное поведение.

Два возможных решения:

  • Переместите код инициализации для gameCore из конструктора в отдельную функцию инициализации, которая должна быть вызвана после полного создания объекта; или
  • Разделите gameCore на два класса: абстрактный интерфейс, реализуемый takeaway, и конкретный класс, содержащий состояние. Сначала создайте takeaway, а затем передайте его (через ссылку на класс интерфейса) конструктору конкретного класса.

Я бы порекомендовал второй, так как это движение к меньшим классам и более слабой связи, и будет сложнее использовать классы неправильно. Первый вариант более подвержен ошибкам, так как нет возможности убедиться, что функция инициализации вызывается правильно.

И последнее замечание: деструктор базового класса обычно должен быть либо виртуальным (чтобы разрешить полиморфное удаление), либо защищенным (чтобы предотвратить недопустимое полиморфное удаление).

person Mike Seymour    schedule 05.10.2011
comment
Это действительно проблема, но не причина ошибки ОП. - person bdonlan; 05.10.2011
comment
@bdonlan: это причина последней ошибки, вызванной попыткой вызова gameCore::initialData(), которая не имеет реализации. - person Mike Seymour; 05.10.2011
comment
нет - это ошибка времени компоновки. Он ничего не знает о порядке инициализации во время выполнения. Если бы такая ошибка возникла, это произошло бы либо во время компиляции, либо во время выполнения. - person bdonlan; 05.10.2011
comment
@bdonlan: компилятор генерирует вызов gameCore::initialData(), поскольку это активное переопределение во время конструктора gameCore. Он не знает, реализована эта функция или нет, поэтому он не может сгенерировать ошибку на этом этапе. Компоновщик обнаруживает, что функция не реализована, и генерирует ошибку, которую мы видим. - person Mike Seymour; 05.10.2011
comment
А, я понимаю, что вы имеете в виду - это чистая виртуальная функция, поэтому (обычно) нет реализации. Компилятор генерирует прямой вызов реализации чистой виртуальной функции (которой обычно не существует), поэтому компоновщик выдает ошибку. Однако компилятор не может ошибиться, потому что технически у вас может быть реализация чистой виртуальной реальности в другой единице трансляции... - person bdonlan; 05.10.2011

Один или несколько ваших файлов .cpp не связаны, или некоторые невстроенные функции в каком-то классе не определены. В частности, не удается найти реализацию takeaway::textualGame(). Обратите внимание, что вы определили textualGame() на верхнем уровне, но это отличается от реализации takeaway::textualGame() - возможно, вы просто забыли там takeaway::.

Ошибка означает, что компоновщик не может найти "vtable" для класса - каждый класс с виртуальными функциями имеет связанную с ним структуру данных "vtable". В GCC эта виртуальная таблица создается в том же файле .cpp, что и первый в списке не встроенный член класса; если нет не встроенных членов, я полагаю, что он будет сгенерирован везде, где вы создаете экземпляр класса. Таким образом, вы, вероятно, не можете связать файл .cpp с первым в списке не встроенным элементом или никогда не определяете этот элемент в первую очередь.

person bdonlan    schedule 05.10.2011
comment
Одна из причин, по которой файл cpp не будет связан, заключается в том, что его забыли добавить в сборку. В моем случае редактирование реализации показало, что объект не был перестроен до начала связывания! (Я дважды и трижды проверил содержимое этого файла cpp, прежде чем заметил, что он не компилируется и не компонуется) - person sage; 22.03.2017

Если класс определяет виртуальные методы вне этого класса, то g++ генерирует виртуальную таблицу только в объектном файле, содержащем внеклассовое определение виртуального метода, который был объявлен первым:

//test.h
struct str
{
   virtual void f();
   virtual void g();
};

//test1.cpp
#include "test.h"
void str::f(){}

//test2.cpp
#include "test.h"
void str::g(){}

Виртуальная таблица будет в test1.o, но не в test2.o

Это оптимизация, которую реализует g++, чтобы избежать необходимости компилировать виртуальные методы, определенные в классе, которые могут быть извлечены vtable.

Описанная вами ошибка ссылки предполагает, что в вашем проекте отсутствует определение виртуального метода (str::f в приведенном выше примере).

person uwedolinsky    schedule 05.10.2011

Вы можете взглянуть на этот ответ на идентичный вопрос (как я понимаю): https://stackoverflow.com/a/1478553 Ссылка, размещенная там, объясняет проблему.

Для быстрого решения вашей проблемы вы должны попробовать что-то вроде этого:

ImplementingClass::virtualFunctionToImplement(){...} Мне очень помогло.

person Abrax    schedule 15.12.2011

Отсутствует реализация функции в классе

Причина, по которой я столкнулся с этой проблемой, заключалась в том, что я удалил реализацию функции из файла cpp, но забыл удалить объявление из файла .h.

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

person sgowd    schedule 14.03.2017

это предполагает, что вы не можете связать явно созданный экземпляр общедоступного игрового ядра базового типа (тогда как заголовочный файл объявляет это).

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

person sehe    schedule 05.10.2011

Компоновщик GNU, в моем случае компаньон GCC 8.1.0, хорошо обнаруживает не переобъявленные чисто виртуальные методы, но при определенной сложности дизайна класса не может определить отсутствующую реализацию методов и ответов с плоской V-таблицей.

или даже имеет тенденцию сообщать об отсутствии реализации, несмотря на то, что она есть.

Тогда единственным решением является проверка согласованности объявления реализации вручную, метод за методом.

person Sam Ginrich    schedule 11.02.2021