Виртуальные таблицы не определены - PullRequest
0 голосов
/ 17 ноября 2009

Я написал код, но не могу его скомпилировать:

#include <cstdio>
#include <vector>

using namespace std;

class Visitor;

class Land {
  public:
    virtual void accept(const Visitor *v);
};

class England : public Land {
  public:
    void accept(const Visitor *v);
};

class Russia : public Land {
  public:
    void accept(const Visitor *v);
};

class Visitor {
  public:
    void visit(const England *e) const;
    void visit(const Russia *r) const;
};

class Trip {
  private:
    vector<Land> *l;
  public:
    explicit Trip(vector<Land> *_l);
    void accept(Visitor *v);
};

/**/

void Visitor::visit(const England *e) const {
  printf("Hey, it's England!\n");
}

void Visitor::visit(const Russia *r) const {
  printf("Hey, it's Russia!\n");
}

void Russia::accept(const Visitor *v) {
  v->visit(this);
}

void England::accept(const Visitor *v) {
  v->visit(this);
}

Trip::Trip(vector<Land> *_l):l(_l) {}

void Trip::accept(Visitor *v) {
  for (unsigned i = 0; i < l->size(); i++) {
    l->at(i).accept(v);
  }
}

int main() {
  England england;
  Russia russia;
  vector<Land> trip_plan;
  trip_plan.push_back(england);
  trip_plan.push_back(russia);
  trip_plan.push_back(england);
  Trip my_trip(&trip_plan);
  Visitor me;
  my_trip.accept(&me);
  return 0;
}

Вот что я получил от g ++:

c++ -ansi -Wall -Wextra -Wconversion -pedantic -Wno-unused-parameter  -o vp vp.cc
/tmp/ccNanCPR.o: In function `Land::Land()':
vp.cc:(.text._ZN4LandC2Ev[Land::Land()]+0xf): undefined reference to `vtable for Land'
/tmp/ccNanCPR.o: In function `Land::Land(Land const&)':
vp.cc:(.text._ZN4LandC1ERKS_[Land::Land(Land const&)]+0x13): undefined reference to `vtable for Land'
/tmp/ccNanCPR.o: In function `Land::~Land()':
vp.cc:(.text._ZN4LandD1Ev[Land::~Land()]+0xf): undefined reference to `vtable for Land'
/tmp/ccNanCPR.o:(.rodata._ZTI6Russia[typeinfo for Russia]+0x10): undefined reference to `typeinfo for Land'
/tmp/ccNanCPR.o:(.rodata._ZTI7England[typeinfo for England]+0x10): undefined reference to `typeinfo for Land'
collect2: ld returned 1 exit status

Этот вопрос основан на Круговых зависимостях объявлений

Ответы [ 7 ]

6 голосов
/ 17 ноября 2009

Я уже ответил это там. Правила создания экземпляров vtable описаны в документации вашего компилятора.

Здесь он ожидает определения (тела) Land :: accept , которое вы объявили не чистым виртуальным, но никогда не определяемым. Либо определите его, либо сделайте его чисто виртуальным.

2 голосов
/ 17 ноября 2009

Если вы не реализуете виртуальную функцию (т. Е. Если она существует для переопределения потомками), вам нужно пометить ее как таковую через '= NULL' (тогда она называется чистой виртуальной функцией).

class Land {
  public:
    virtual void accept(const Visitor *v)= 0;   // pure virtual function
};
1 голос
/ 17 ноября 2009

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

Член accept () для России и Англии, с другой стороны, один и тот же, и его следует перенести на Землю.

Вот как я бы это структурировал (взгляните на использование accept (), receive () и name ()):

#include <cstdio>
#include <vector>

using namespace std;

class Visitor;

class Land {

  public:
    virtual void accept(const Visitor *v); 

    virtual void arrive(void) const = 0;
    virtual const char *name(void) const = 0;

};

class England : public Land {
  public:
    void arrive(void) const;
    const char *name(void) const;
};

class Russia : public Land {
  public:
    void arrive(void) const;
    const char *name(void) const;
};

class Visitor {
  public:
    void visit(const Land *l) const;
};


class Trip {
  private:
    vector<Land *> *l;  

  public:
    Trip(vector<Land *> *_l);   
    void accept(Visitor *v);
};


/**** Implementations  *****/

// underlying Land

void Land::accept(const Visitor *v) {
  v->visit(this);
}


// England

const char *England::name(void) const {
  return "England"; 
}

void England::arrive(void) const {
  printf("Welcome to our lovely country, your passport please\n");
}


// Russia

const char *Russia::name(void) const {
  return "Russia"; 
}

void Russia::arrive(void) const {
  printf("Passport!!\n");
}


// Visitor

void Visitor::visit(const Land *l) const {
  l->arrive();
  printf("Hey, it'm in %s!\n", l->name());
}



// Trip

Trip::Trip(vector<Land *> *_l) 
  : l(_l)   // !!! <Land *>
{

}

void Trip::accept(Visitor *v) {

  for (unsigned i = 0; i < l->size(); i++) {    
    l->at(i)->accept(v);                        
  }
}



/**** main *****/

int main() 
{
  England england;
  Russia russia;

  vector<Land *> trip_plan;     

  trip_plan.push_back(&england);    
  trip_plan.push_back(&russia);     
  trip_plan.push_back(&england);

  Trip my_trip(&trip_plan);
  Visitor me;
  my_trip.accept(&me);

  return 0;
}
1 голос
/ 17 ноября 2009

Реализуйте метод Land :: accept или объявите его как чисто виртуальный.

Однако я обнаружил возможную проблему в основном:

trip_plan.push_back(england);
trip_plan.push_back(russia);
trip_plan.push_back(england);

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

0 голосов
/ 17 ноября 2009

Хорошо, вот полный рабочий образец (не уверен, что ваш вырезан из вашего). Это компилируется здесь, и я пометил все места, где я внес изменения, с помощью "!!!" комментарии:

#include <cstdio>
#include <vector>

using namespace std;

class Visitor;

class Land {
  public:
    virtual void accept(const Visitor *v)= 0;  // !!! = 0
};

class England : public Land {
  public:
    void accept(const Visitor *v);
};

class Russia : public Land {
  public:
    void accept(const Visitor *v);
};

class Visitor {
  public:
    void visit(const England *e) const;
    void visit(const Russia *r) const;
};


class Trip {
  private:
    vector<Land *> *l;          // !!! <Land *>

  public:
    Trip(vector<Land *> *_l);   // !!! <Land *>
    void accept(Visitor *v);
};


/* Implementations */

void Visitor::visit(const England *e) const {
  printf("Hey, it's England!\n");
}

void Visitor::visit(const Russia *r) const {
  printf("Hey, it's Russia!\n");
}

void Russia::accept(const Visitor *v) {
  v->visit(this);
}

void England::accept(const Visitor *v) {
  v->visit(this);
}

Trip::Trip(vector<Land *> *_l) : l(_l)   // !!! <Land *>
{

}

void Trip::accept(Visitor *v) {

  for (unsigned i = 0; i < l->size(); i++) {        // !!! i < l->size()
    l->at(i)->accept(v);                            // !!! at(i)->accept()
  }
}

int main() 
{
  England england;
  Russia russia;

  vector<Land *> trip_plan;         // !!! <Land *>

  trip_plan.push_back(&england);    // !!! &england
  trip_plan.push_back(&russia);     // !!! &russia
  trip_plan.push_back(&england);    // !!! &england

  Trip my_trip(&trip_plan);
  Visitor me;
  my_trip.accept(&me);

  return 0;
}
0 голосов
/ 17 ноября 2009

Я думаю, что там, где вы используете vector, вы должны использовать std::vector<Land*>:

class Trip {
  private:
    std::vector<Land*> *l;   // vector of pointers to Land
  public:
    explicit Trip(std::vector<Land*> *_l);
    void accept(Visitor *v);
};

и

void Trip::accept(Visitor *v) {
  for (unsigned i = 0; i< l->size(); i++) {
    l->at(i)->accept(v);  // . changed to ->
  }
}

и

int main() {
  England england;
  Russia russia;
  std::vector<Land*> trip_plan;
  trip_plan.push_back(&england);   // push_back pointers
  trip_plan.push_back(&russia);
  trip_plan.push_back(&england);
  Trip my_trip(&trip_plan);
  Visitor me;
  my_trip.accept(&me);
  return 0;
}

Вам нужно использовать <Land*>, чтобы не разрезать england и russia на экземпляры Land. Кроме того, вы можете подумать об использовании итератора в Trip::accept в следующий раз.

0 голосов
/ 17 ноября 2009

Я вижу еще две вещи:

1) обратите внимание на пропущенное «<» перед размером </p>

void Trip::accept(Visitor *v) {
  for (unsigned i = 0; i < size(); i++) {
    l->at(i).accept(v);
  }
}

2) Я думаю (если я правильно понимаю ваш пример) вектор должен быть вектором <Земля> (вы строите вектор абстрактных Земель, который затем заполняется указателями на конкретные объекты Земли)

 vector<Land> Trip;

или

 typedef vector<Land> trip_t;  // type for a trip is a vector of Lands
 ... 

 trip_t Trip;

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

...