虚拟表未定义

时间:2009-11-17 13:21:42

标签: c++ design-patterns linker vtable

我写了一些代码,但我无法编译它:

#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

此问题基于Circular dependencies of declarations

7 个答案:

答案 0 :(得分:6)

我已经answered了。 vtable实例化的规则在编译器文档中有解释。

在这里,它正在等待 Land :: accept 的定义(正文),你声明它是非纯虚拟的,但从未定义过。 定义它,或使其成为纯虚拟。

答案 1 :(得分:2)

如果你没有实现一个虚函数(即它是否被后代覆盖),你需要通过'= NULL'将它标记为它(然后它被称为纯虚函数)。

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

答案 2 :(得分:1)

实施Land :: accept方法或将其声明为纯虚拟。

但是,我发现主要可能存在问题:

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

我不知道什么类型的向量,但是您可能会遇到一个问题,即将派生类对象插入基类值的向量中(Type Slicing)。

答案 3 :(得分:1)

这可能远远超出你的要求,但从设计的角度来看,我认为特定于土地的东西应该在每个土地的类中,即它让我有点烦恼看到重载的visit()函数访客。

另一方面,俄罗斯和英格兰的接受()成员是相同的,应该被移到土地上。

以下是我将如何构建它(看看使用accept(),arri()和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;
}

答案 4 :(得分:0)

我还看到了两件事:

1)注意缺少“&lt;”尺寸之前

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

2)我认为(假设我理解你的例子)矢量应该是vector&lt;土地&gt; (你正在构建一个抽象的Lands矢量,然后用指向具体Land对象的指针填充)

 vector<Land> Trip;

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

 trip_t Trip;

(在我撰写此评论时,您似乎仍在编辑示例,因此我将不得不采用更一般的答案)。

答案 5 :(得分:0)

我认为您使用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*>,这样才不会将englandrussia切片为Land的实例。 另外,您可能会考虑下次在Trip::accept中使用迭代器。

答案 6 :(得分:0)

好的,这是一个完整的工作样本(不确定你的剪辑是否已被剪裁)。它在这里编译,我用“!!!”标记了我所做的所有改动的地方注释:

#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;
}