c++ 声明的循环依赖性[重复]

jvlzgdj9  于 12个月前  发布在  其他
关注(0)|答案(6)|浏览(145)

此问题在此处已有答案

Resolve build errors due to circular dependency amongst classes(12个答案)
11天前关闭
我试图实现访问者模式的例子,但是我在类声明的循环依赖方面遇到了麻烦。当我做Visitor类的前向声明时,类Russia和England不知道Visitor有方法visit,但是当我扩展Visitor的前向声明为方法accept时,我需要使用类England和Russia,但是它们需要知道Visitor是谁,因为他们在他们的代码中使用这种类型。我尝试了许多排序代码的变化,但我完全失败了。请帮助我理解,C++需要什么来获得这个。谢谢。

#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) {
      v->visit(this);
    }
};

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

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

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

class Trip {
  private:
    vector<Land> *l;
  public:
    explicit Trip(vector<Land> *_l):l(_l) {}
    void 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
vp.cc: In member function ‘virtual void England::accept(const Visitor*)’:
vp.cc:40: error: invalid use of incomplete type ‘const struct Visitor’
vp.cc:30: error: forward declaration of ‘const struct Visitor’
vp.cc: In member function ‘virtual void Russia::accept(const Visitor*)’:
vp.cc:47: error: invalid use of incomplete type ‘const struct Visitor’
vp.cc:30: error: forward declaration of ‘const struct Visitor’

u91tlkcl

u91tlkcl1#

class Visitor; 

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

// Define Visitor
class Visitor {
  //...
};

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

字符串

wfauudbj

wfauudbj2#

亚历克斯已经给出了答案的一部分。
但是,如果你不打算实现土地接受,那么你需要:

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

字符串

anhgbhbe

anhgbhbe3#

阿列克谢·马利斯托夫的回答确实解决了你的问题,但它也暴露了下一个问题。
gcc编译器正在抱怨vtable(它用于带有虚函数的类,以及其他东西)。它使用的规则有文档记录(参见文档):
如果类声明了任何非内联、非纯虚函数,则第一个虚函数将被选为类的“键方法”,并且vtable仅在定义键方法的翻译单元中发出。
现在,Alexey的类版本定义了非内联、非纯虚函数 accept。因此,gcc推迟了 Land vtable的示例化,直到它看到 Land::accept 的定义。添加它,看看它是否有效。或者,正如Nicholaz所说,只是使它成为纯虚函数。
我不想“解决”问题,我想知道什么是错的,为什么
习惯于将声明与定义分开。除了模板的特殊情况,C++倾向于以这种方式工作得更好。

gzszwxb4

gzszwxb44#

当你转发声明时,C++编译器知道有这种类型的用户定义类型,但它不知道它的数据成员和方法。为了使用这种用户定义类型的全部功能,您需要在使用它们之前包含它的头文件,其中包含所有它的方法和数据成员,否则您只需进行正向声明,并使用它的方法和数据成员。包含头文件。在你的例子中,你调用了forward声明的Visitor类的visit()方法,这样你就通知了编译器有一个Visitor数据类型,但是编译器还不知道visit()方法。为了解决这个问题,你必须删除forward声明,把Visitor定义放在所有类的顶部。你会得到这样的东西

#include <cstdio>
#include <vector>

using namespace std;
class England;
class Russia;
class Visitor {
  public:
    void visit(const England *e) const {
      printf("Hey, it's England!\n");
    }

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

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

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

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

字符串

92vpleto

92vpleto5#

我已经很久没有写过复杂的C++程序了,但是如果我没记错的话,你应该把.h文件中的那些类的 backbone 和这个.c文件同名,然后把它包含到这个.c文件中。
希望这对你有帮助。

p3rjfoxz

p3rjfoxz6#

给予所有类类型声明之前,它的使用..我认为这将工作。

相关问题