未明确引用'vtable for xxx'

时间:2011-10-05 17:24:41

标签: c++ linker linker-errors vtable

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

我不断从链接器中获取此错误,我知道它与内联函数有关,可以暂时存储vtable。但是这需要我不太确定。我认为这与我如何在takeaway.cpp的启动列表中调用gameCore的构造函数有关。

我有一个模板化的类(gameCore.h) 和一个继承自gameCore的类(takeaway.cpp) 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

takeaway.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

7 个答案:

答案 0 :(得分:34)

未链接到一个或多个.cpp文件,或者未定义某些类中的某些非内联函数。特别是,找不到takeaway::textualGame()的实现。请注意,您已在顶层定义了textualGame(),但这与takeaway::textualGame()实施不同 - 可能您只是忘记了takeaway::

错误的含义是链接器无法找到类的“vtable” - 每个具有虚函数的类都具有与之关联的“vtable”数据结构。在GCC中,此vtable在与该类的第一个列出的非内联成员相同的.cpp文件中生成;如果没有非内联成员,我会相信,无论你在哪里实例化它,都会生成它。因此,您可能无法将.cpp文件与首先列出的非内联成员链接起来,或者从不首先定义该成员。

答案 1 :(得分:26)

缺少vtable的第一组错误是由于您未实现takeaway::textualGame()而引起的。相反,您实现了非成员函数textualGame()。我认为添加缺失的takeaway::将解决这个问题。

上一个错误的原因是您正在从initialData()的构造函数调用虚函数gameCore。在此阶段,根据当前构造的类型(gameCore),派生的类(takeaway)调度虚函数。这个特殊的函数是纯虚函数,所以在这里调用它会给出不确定的行为。

两种可能的解决方案:

  • gameCore的初始化代码移出构造函数并移入单独的初始化函数,该函数必须在完全构造对象后调用;或
  • gameCore分成两个类:一个由takeaway实现的抽象接口,以及一个包含该状态的具体类。首先构造takeaway,然后将它(通过对接口类的引用)传递给具体类的构造函数。

我会推荐第二个,因为它是向更小的类和更松散的耦合的转变,并且将更难以错误地使用类。第一个更容易出错,因为无法确定是否正确调用了初始化函数。

最后一点:基类的析构函数通常应该是虚拟的(允许多态删除)或受保护的(以防止无效的多态删除)。

答案 2 :(得分:7)

如果一个类定义了该类之外的虚方法,那么g ++只在包含首先声明的虚方法的类外定义的目标文件中生成vtable:

//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(){}

vtable将在test1.o中,但不在test2.o

这是g ++实现的优化,以避免编译将被vtable引入的类内定义的虚拟方法。

您描述的链接错误表明您的项目中缺少虚拟方法的定义(上例中的str :: f)。

答案 3 :(得分:3)

您可以查看相同问题的答案(据我了解): https://stackoverflow.com/a/1478553 那里发布的链接解释了这个问题。

为了快速解决您的问题,您应该尝试编写类似的代码:

ImplementingClass::virtualFunctionToImplement(){...} 它对我帮助很大。

答案 4 :(得分:1)

它表明您未能链接显式实例化的basetype public gameCore(而头文件forward将声明它)。

由于我们对您的构建配置/库依赖项一无所知,我们无法确定哪些链接标记/源文件丢失,但我希望单独的提示可以帮助您修复ti。

答案 5 :(得分:1)

在课程

中缺少某个功能的实现

我遇到这个问题的原因是因为我从cpp文件中删除了函数的实现,但忘记从.h文件中删除声明。

我的回答没有具体回答你的问题,但是让那些来这个帖子寻找答案的人知道这也是一个原因。

答案 6 :(得分:0)

GNU 链接器,在我的例子中是 GCC 8.1.0 的伴侣,可以很好地检测到没有重新声明的纯虚方法,但是在类设计的某些复杂性之上,它无法识别方法的缺失实现和带有扁平“V 表”的答案失踪”,

甚至倾向于报告缺失的实现,尽管它存在。

唯一的解决方案是手动、逐个方法地验证实现声明的一致性。