【问题标题】:Circular dependencies of declarations声明的循环依赖
【发布时间】:2010-12-17 10:39:50
【问题描述】:

我正在尝试实现访问者模式的示例,但我遇到了类声明的循环依赖问题。当我做访问者类的前向声明时,俄罗斯和英格兰类不知道访问者有方法访问,但是当我将访问者的前向声明扩展为方法接受时,我需要使用英格兰和俄罗斯类,但他们需要知道谁访问者是,因为他们在他们的代码中使用这种类型。我尝试了许多订购代码的变体,但我完全失败了。请帮助我理解,C++ 需要什么来获得这个。谢谢。

#include #include 使用命名空间标准; 类访客; 类土地{ 民众: virtual void accept(const 访问者 *v); }; 英格兰类:公共土地{ 民众: 无效接受(常量访问者 *v){ v->访问(这个); } }; 俄罗斯类:公共土地{ 民众: 无效接受(常量访问者 *v){ v->访问(这个); } }; 类访客{ 民众: 无效访问(常量英格兰 *e)常量 { printf("嘿,这里是英格兰!\n"); } 无效访问(常量俄罗斯 *r)常量 { printf("嘿,这是俄罗斯!\n"); } }; 班级旅行 { 私人的: 矢量 *l; 民众: 显式旅行(矢量 *_l):l(_l){} 无效接受(访客*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 vp.cc:在成员函数“virtual void England::accept(const Visitor*)”中: vp.cc:40:错误:不完整类型“const struct Visitor”的无效使用 vp.cc:30:错误:“const struct Visitor”的前向声明 vp.cc:在成员函数“virtual void Russia::accept(const Visitor*)”中: vp.cc:47:错误:不完整类型“const struct Visitor”的无效使用 vp.cc:30:错误:“const struct Visitor”的前向声明

【问题讨论】:

    标签: c++ design-patterns


    【解决方案1】:

    我已经很久没有写过复杂的 C++ 程序了,但是如果我没记错的话,你应该在 .h 文件中与这个 .c 文件同名的那些类的骨架。然后将其包含到这个.c 文件中。

    希望这会有所帮助。

    【讨论】:

    • 好吧,我不想“解决”这个问题。我想了解什么是错的以及为什么。不过谢谢。
    【解决方案2】:
    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);
    }
    

    【讨论】:

    • 恐怕这并不能解决问题。现在我得到了很多——未定义的对 Land' 的 vtable for Land', and undefined reference to typeinfo 的引用。
    • 你真的在任何地方定义了类 Land 吗?我想你可能想让 Land 成为纯虚拟的:class Land { public: virtual ~Land() {} virtual void accept(const Visitor *v) = 0; };您看到的错误是 GCC 没有为 Land 创建 vtable,因为您从未为它定义任何函数。
    • 好吧,Alexey 解决了这个问题,但出现了新问题:stackoverflow.com/questions/1748827/…
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      Alexy 已经给出了部分答案。

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

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

      【讨论】:

      【解决方案5】:

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

      gcc 编译器抱怨 vtable(用于具有虚函数的类等)。它使用的规则已记录在案(请参阅docs):

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

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

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

      习惯于将声明与定义分开。除了模板的特殊情况,C++ 倾向于以这种方式更好地工作。

      【讨论】:

        【解决方案6】:

        当您转发声明时,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);
            }
        };
        ...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-10-08
          • 2012-04-24
          • 1970-01-01
          • 1970-01-01
          • 2015-08-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多