wake-up-neo.com

Undefinierter Verweis auf 'vtable for 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

Ich erhalte diesen Fehler vom Linker, ich weiß, dass er etwas mit Inline-Funktionen zu tun hat, die eine vtable temporär speichern. Aber was das mit sich bringt, weiß ich nicht so genau. Ich würde annehmen, dass es etwas damit zu tun hat, wie ich den Konstruktor von gameCore in der Initialisierungsliste von takeaway.cpp aufrufe

Ich habe eine Klasse mit Vorlagen (gameCore.h) und eine Klasse (takeaway.cpp), die von gameCore erbt. Der vtable-Fehler wird 3 mal 1) im takeeaways-Konstruktor 2) im takeeaways-Destruktor 3) im gameCores-Konstruktor aufgerufen

Ich benutze G ++ Hier ist der Code: (Ich weiß, es mag schwer zu lesen scheinen, aber ich habe genau markiert, wo die Fehler auftreten) 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
37
Man

Der erste Satz von Fehlern für die fehlende vtable wird verursacht, weil Sie takeaway::textualGame() nicht implementieren. Stattdessen implementieren Sie eine Nicht-Member-Funktion, textualGame(). Ich denke, dass das Hinzufügen des fehlenden takeaway:: Das beheben wird.

Die Ursache für den letzten Fehler ist, dass Sie eine virtuelle Funktion, initialData(), aus dem Konstruktor von gameCore aufrufen. In diesem Stadium werden virtuelle Funktionen entsprechend dem Typ, der gerade erstellt wird, verteilt (gameCore), nicht die am meisten abgeleitete Klasse (takeaway). Diese spezielle Funktion ist rein virtuell, und daher führt der Aufruf hier zu undefiniertem Verhalten.

Zwei mögliche Lösungen:

  • Verschieben Sie den Initialisierungscode für gameCore aus dem Konstruktor in eine separate Initialisierungsfunktion, die aufgerufen werden muss nach das Objekt ist vollständig konstruiert; oder
  • Teilen Sie gameCore in zwei Klassen auf: eine abstrakte Schnittstelle, die von takeaway implementiert werden soll, und eine konkrete Klasse, die den Zustand enthält. Konstruieren Sie zuerst takeaway und übergeben Sie es dann (über einen Verweis auf die Interface-Klasse) an den Konstruktor der konkreten Klasse.

Ich würde die zweite empfehlen, da es sich um eine Bewegung in Richtung kleinerer Klassen und lockerer Kopplung handelt und es schwieriger sein wird, die Klassen falsch zu verwenden. Die erste ist fehleranfälliger, da nicht sichergestellt werden kann, dass die Initialisierungsfunktion korrekt aufgerufen wird.

Ein letzter Punkt: Der Destruktor einer Basisklasse sollte normalerweise entweder virtuell (um polymorphes Löschen zu ermöglichen) oder geschützt (um ungültiges polymorphes Löschen zu verhindern) sein.

27
Mike Seymour

Mindestens eine Ihrer CPP-Dateien wird nicht eingebunden, oder einige Nicht-Inline-Funktionen in einer Klasse sind nicht definiert. Insbesondere kann die Implementierung von takeaway::textualGame() nicht gefunden werden. Beachten Sie, dass Sie eine textualGame() auf oberster Ebene definiert haben, dies unterscheidet sich jedoch von einer takeaway::textualGame() -Implementierung - wahrscheinlich haben Sie gerade die takeaway:: Dort.

Der Fehler bedeutet, dass der Linker die "vtable" für eine Klasse nicht finden kann - jeder Klasse mit virtuellen Funktionen ist eine "vtable" -Datenstruktur zugeordnet. In GCC wird diese vtable in derselben CPP-Datei wie das erste aufgelistete Nicht-Inline-Mitglied der Klasse generiert. Wenn es keine Nicht-Inline-Mitglieder gibt, wird es generiert, wo immer Sie die Klasse instanziieren, glaube ich. Sie können die CPP-Datei also wahrscheinlich nicht mit dem zuerst aufgelisteten Nicht-Inline-Mitglied verknüpfen oder dieses Mitglied erst gar nicht definieren.

34
bdonlan

Wenn eine Klasse virtuelle Methoden außerhalb dieser Klasse definiert, generiert g ++ die vtable nur in der Objektdatei, die die Definition der virtuellen Methode außerhalb der Klasse enthält, die zuerst deklariert wurde:

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

Die vtable befindet sich in test1.o, jedoch nicht in test2.o

Dies ist eine Optimierung, die von g ++ implementiert wird, um zu vermeiden, dass in der Klasse definierte virtuelle Methoden kompiliert werden müssen, die von der vtable abgerufen würden.

Der Linkfehler, den Sie beschreiben, deutet darauf hin, dass die Definition einer virtuellen Methode (str :: f im obigen Beispiel) in Ihrem Projekt fehlt.

7
uwedolinsky

Sie können sich diese Antwort auf eine identische Frage ansehen (soweit ich weiß): https://stackoverflow.com/a/147855 Der dort veröffentlichte Link erklärt das Problem.

Um Ihr Problem schnell zu lösen, sollten Sie versuchen, Folgendes zu codieren:

ImplementingClass::virtualFunctionToImplement(){...} Es hat mir sehr geholfen.

3
Abrax

Fehlende Implementierung einer Funktion in der Klasse

Der Grund, warum ich auf dieses Problem stieß, war, dass ich die Implementierung der Funktion aus der CPP-Datei gelöscht hatte, aber vergessen hatte, die Deklaration aus der H-Datei zu löschen.

Meine Antwort beantwortet Ihre Frage nicht speziell, lässt aber die Leute, die zu diesem Thread kommen und nach Antworten suchen, wissen, dass dies auch eine Ursache sein kann.

1
sgowd

es deutet darauf hin, dass Sie den explizit instanziierten Basistyp public gameCore nicht verknüpfen können (während die Header-Datei dies deklariert).

Da wir nichts über Ihre Build-Konfigurations-/Bibliotheksabhängigkeiten wissen, können wir nicht wirklich sagen, welche Link-Flags/Quelldateien fehlen, aber ich hoffe, der Hinweis allein hilft Ihnen, das Problem zu beheben.

1
sehe