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