【问题标题】:Undefined reference to 'vtable for xxx'未定义对 'vtable for xxx' 的引用
【发布时间】:2011-12-01 16:27:57
【问题描述】:
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++ 这是代码: (我知道它可能看起来很难阅读,但我已经明确地标出了错误发生的位置) 外卖.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

【问题讨论】:

  • 您需要发布一个示例,而不仅仅是吐出所有代码。
  • 这是一个很好的问题,但是对于很多代码来说,尝试缩小范围,以便我们可以轻松了解发生了什么以及问题。
  • 你能试着把你的代码简化成一个重现问题的小例子吗?那会帮助人们帮助你。它甚至可以帮助你自己弄清楚。而且,呃,这里真的需要修订历史cmet吗?
  • 我想补充一点,有时在派生类的析构函数没有定义的时候会出现这个错误。
  • 天哪,这个问题怎么会有这么多赞成票。如果只有错误日志,它可能会更好地为用户服务,而不是最好的答案是@bdonlan 的答案的第一句话。

标签: c++ linker linker-errors vtable


【解决方案1】:

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

错误的意思是链接器找不到类的“vtable”——每个具有虚函数的类都有一个与之关联的“vtable”数据结构。在 GCC 中,这个 vtable 是在与类的第一个列出的非内联成员相同的 .cpp 文件中生成的;如果没有非内联成员,我相信它会在你实例化类的任何地方生成。因此,您可能无法将 .cpp 文件与第一个列出的非内联成员链接,或者从一开始就没有定义该成员。

【讨论】:

  • 无法链接 cpp 文件的一个原因是忘记将其添加到构建中。在我的例子中,编辑实现发现在链接开始之前对象没有被重建! (在注意到它没有被编译或链接之前,我已经两次和三次检查了该 cpp 文件的内容)
【解决方案2】:

第一组错误,对于丢失的vtable,是因为你没有实现takeaway::textualGame();相反,您实现了一个非成员函数textualGame()。我认为添加缺少的takeaway:: 可以解决这个问题。

最后一个错误的原因是你从gameCore的构造函数中调用了一个虚函数initialData()。在这个阶段,根据当前正在构造的类型 (gameCore) 调度虚函数,不是派生最多的类 (takeaway)。这个特定的函数是纯虚函数,因此在这里调用它会产生未定义的行为。

两种可能的解决方案:

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

我会推荐第二种,因为它是向更小的类和更松散的耦合方向发展,并且更难错误地使用这些类。第一个更容易出错,因为无法确保初始化函数被正确调用。

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

【讨论】:

  • 这确实是一个问题,但不是OP错误的原因
  • @bdonlan:这是最后一个错误的原因,是由于尝试调用没有实现的gameCore::initialData()引起的。
  • 不——这是一个链接时错误。它对运行时初始化排序一无所知。如果发生这样的错误,它会在编译时或运行时发生。
  • @bdonlan:编译器生成对gameCore::initialData() 的调用,因为这是gameCore 的构造函数期间的活动覆盖。它不知道该功能是否实现,因此在该阶段它不会产生错误。链接器发现函数没有实现,并产生我们看到的错误。
  • 啊,我明白你的意思了——它是一个 pure 虚函数,所以(通常)没有实现。编译器生成对纯虚函数实现的直接调用(通常不存在),因此链接器出错。不过,编译器不会出错,因为从技术上讲,您可以在另一个翻译单元中实现纯虚拟...
【解决方案3】:

如果一个类在该类之外定义了虚方法,则 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)。

【讨论】:

    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

      类中缺少函数的实现

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

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

      【讨论】:

        【解决方案6】:

        这表明您无法链接显式实例化的基本类型 public gameCore(而头文件 forward 声明了它)。

        由于我们对您的构建配置/库依赖项一无所知,因此我们无法确定缺少哪些链接标志/源文件,但我希望仅凭提示可以帮助您修复 ti。

        【讨论】:

          【解决方案7】:

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

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

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

          【讨论】:

            【解决方案8】:

            如果你有虚拟解构函数,你需要这样写: ~SubListener() override = default;,别忘了这个=default

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-11-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-08-14
              相关资源
              最近更新 更多