【问题标题】:Virtual tables are undefined虚拟表未定义
【发布时间】:2010-12-17 10:57:06
【问题描述】:

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

#include #include 使用命名空间标准; 类访客; 类土地{ 民众: virtual void accept(const 访问者 *v); }; 英格兰类:公共土地{ 民众: 无效接受(常量访问者 *v); }; 俄罗斯类:公共土地{ 民众: 无效接受(常量访问者 *v); }; 类访客{ 民众: 无效访问(常量英格兰 *e)常量; 无效访问(常量俄罗斯 *r)常量; }; 班级旅行 { 私人的: 矢量 *l; 民众: 显式旅行(矢量 *_l); 无效接受(访客*v); }; /**/ void 访客::visit(const England *e) const { printf("嘿,这里是英格兰!\n"); } void 访客::visit(const 俄罗斯 *r) const { printf("嘿,这是俄罗斯!\n"); } 无效俄罗斯::accept(const 访问者 *v) { v->访问(这个); } void England::accept(const Visitor *v) { v->访问(这个); } Trip::Trip(vector *_l):l(_l) {} void Trip::accept(Visitor *v) { for (unsigned i = 0; i size(); i++) { l->at(i).accept(v); } } int main() { 英格兰英格兰; 俄罗斯俄罗斯; 矢量 旅行计划; trip_plan.push_back(英格兰); trip_plan.push_back(俄罗斯); trip_plan.push_back(英格兰); 行程 my_trip(&trip_plan); 拜访我; my_trip.accept(&me); 返回0; }

这是我从 g++ 得到的:

c++ -ansi -Wall -Wextra -Wconversion -pedantic -Wno-unused-parameter -o vp vp.cc /tmp/ccNanCPR.o:在函数“Land::Land()”中: vp.cc:(.text._ZN4LandC2Ev[Land::Land()]+0xf): undefined reference to `vtable for Land' /tmp/ccNanCPR.o:在函数“Land::Land(Land const&)”中: vp.cc:(.text._ZN4LandC1ERKS_[Land::Land(Land const&)]+0x13): 未定义对“vtable for Land”的引用 /tmp/ccNanCPR.o:在函数“Land::~Land()”中: vp.cc:(.text._ZN4LandD1Ev[Land::~Land()]+0xf): 未定义对“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 返回 1 个退出状态

本题基于Circular dependencies of declarations

【问题讨论】:

标签: c++ design-patterns linker vtable


【解决方案1】:

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

另一方面,俄罗斯和英国的 accept() 成员是相同的,应该向上移动到 Land 中。

这是我将如何构建它(看看accept()、arrange()和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;
}

【讨论】:

    【解决方案2】:

    我认为您使用vector 的地方应该使用std::vector&lt;Land*&gt;

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

    您需要使用&lt;Land*&gt;,这样您就不会将englandrussia 分割成Land 的实例。 另外,下次您可能会考虑在Trip::accept 中使用迭代器。

    【讨论】:

      【解决方案3】:

      好的,这是一个完整的工作示例(不确定您是否从您的示例中截取)。它在这里编译,我用“!!!”标记了我进行更改的所有地方厘米:

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

      【讨论】:

        【解决方案4】:

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

        但是,我在 main 中发现了一个可能的问题:

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

        我不知道什么是类型向量,但您可能在提供要插入基类值向量中的派生类对象时遇到问题(类型切片)。

        【讨论】:

        • 是的,甚至还有一个这样的问题:stackoverflow.com/questions/274626/…
        • 是的,我有这个问题,但我使用了一些指针来摆脱它。但这让我困惑了一段时间。
        • 这个词我记不住了!切片!
        【解决方案5】:

        如果您没有实现虚函数(即,如果它被后代覆盖),则需要通过 '=NULL' 将其标记为此类(然后称为纯虚函数)。

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

        【讨论】:

        • 我认为常见的用法是 = 0。虽然在 C++ 中 NULL 被定义为 0,但你可能会用这种表示法混淆其他人,混淆是不好的。
        • 谢谢。我不知道 '=0' 标记函数是纯虚拟的。下一个教训。
        【解决方案6】:

        我看到了另外两件事:

        1) 注意大小前缺少的“

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

        2)我认为(假设我正确理解了您的示例)向量应该是 vector(您正在构建一个抽象 Lands 的向量,然后填充指向具体 Land 对象的指针)

         vector<Land> Trip;
        

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

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

        【讨论】:

        • 对不起.. 强大的 stackoverflow 文本处理无法处理
        【解决方案7】:

        我已经在那里answered 了。 vtable 实例化的规则在您的编译器文档中进行了说明。

        在这里,它正在等待查看 Land::accept 的定义(主体),您声明它是一个非纯虚拟,但从未定义。 要么定义它,要么让它成为纯虚拟的。

        【讨论】:

        • 我想我现在明白了。非常感谢。
        猜你喜欢
        • 1970-01-01
        • 2021-07-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-14
        • 1970-01-01
        • 1970-01-01
        • 2012-09-16
        • 2015-08-22
        相关资源
        最近更新 更多