声明的循环依赖

时间:2009-11-17 12:44:34

标签: c++ design-patterns

我正在尝试实现访问者模式的示例,但是我对类的声明的循环依赖有困难。在进行上课访客的申报的时候,俄罗斯和英格兰的班级不知道访问者有方法访问,但是在向方法接受扩展访问者的申报的时候,需要使用英国和俄罗斯的班级,但是他们需要知道谁访问者是,因为他们在代码中使用此类型。我尝试了许多订购代码的变体,但我完全失败了。请帮助我理解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’

6 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:1)

Alexy已经给出了答案的一部分。

但是,如果您不打算接受Land,那么您需要:

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

答案 2 :(得分:1)

Alexey Malistov的回答 解决了你的问题。 它也暴露了下一个问题。

gcc编译器抱怨vtable(用于具有虚函数的类,以及其他内容)。它使用的规则记录在案(见docs):

  

如果类声明任何非内联非纯虚函数,则选择第一个作为类的“键方法”,并且仅在定义键方法的转换单元中发出vtable。

现在,Alexey的类版本定义了非内联非纯虚函数 accept 。因此,gcc推迟 Land vtable的实例化,直到它看到 Land :: accept 的定义。添加它,看看它是否有效。或者,正如Nicholaz所说,只是让它变成纯粹的虚拟。

  
    

好吧,我不想“解决”这个问题。我想知道什么是错的,为什么

  

习惯于从定义中分离声明。除了模板的特殊情况,C ++往往更好地工作。

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

我没有在很长一段时间内编写复杂的C ++程序,但如果我没记错的话,你应该在.h文件中使用与此.c文件同名的那些类的框架。然后将其包含在此.c文件中。

希望这有帮助。

答案 5 :(得分:0)

在使用之前给出所有类类型声明。我认为它会起作用。