【问题标题】:C++ Errors: " 'class' does not name a type" and "invalid use of incomplete type ‘struct ...' "C++ 错误:“‘类’没有命名类型”和“无效使用不完整类型‘struct ...’”
【发布时间】:2014-01-28 08:20:48
【问题描述】:

这是一个非常重复的问题,也在 StackOverflow 中,但即使尝试不同的答案,我也无法解决我的问题。所以,我有一些课程:

main.cpp:

#include "foo.h"
#include "bar.h"

...

foo.h:

#include "bar.h"
class foo {
  foo();
  bar& bind(bar &b);
  gru& bind(gru &g);
};

bar.h:

#include "foo.h"
class bar {
  bar();
  foo& bind(foo &f);
  gru& bind(gru &g);
};

显然,我有一个循环依赖。所以我得到了臭名昭著的错误'bar' does not name a type。在这种情况下,我将class bar; 添加到foo 声明并删除#include。当我这样做时,我得到:invalid use of incomplete type ‘struct bar'

我尝试了一些不同的方法,也将class foo; 添加到 bar,但我总是遇到某种错误。在最后一种情况下,我得到:

bar.cpp:48:11: error: prototype for ‘foo& bar::bind(bar::foo&)’ does not match any in class ‘bar’
bar.h:55:12: error: candidates are: gru& bar::bind(gru&)
bar.h:54:13: error:                 bar::foo& bar::bind(bar::foo&)

另外,我从来没有收到任何关于 gru 的抱怨。作为附加信息,在我添加 foo 之前,该程序中已经存在,与 barmain 完美配合。

有什么有用的想法吗?非常感谢:)

【问题讨论】:

  • In this case, I add class bar; to foo declaration and delete the #include. When I do that, I get: invalid use of incomplete type ‘struct bar'. 据我所知,这应该已经足够了。这是你真正的测试用例吗?您能在我们眼前编译和运行的实时代码粘贴网站上向我们展示吗?
  • 在您发布的代码中没有无效使用不完整类型。您在哪里以及如何以需要完整类型的方式使用 bar

标签: c++ types compilation include cyclic-dependency


【解决方案1】:

C++ 编译器非常古老,而且在您的代码中处理此示例的部分不是编译器本身,而是编译器链的一部分,称为 预处理器。如果我们可以同意 C++ 编译器不是很聪明,那么预处理器就是一个乏味但友好的助手。

*.cpp 文件被编译,然后与程序中的其他 *.cpp 文件链接。 .h 文件本身实际上没有任何意义:这种分离据说对程序员来说是一个优势。这里的关键点是,无论您在 *.cpp 文件中包含多少 *.h 文件,最后都会生成一个巨大的、生成的 cpp 文件,其中包含您的 *.h 文件中存在的整个代码,以及*.cpp 文件本身中的源代码。

可以通过多种方式解决循环依赖关系,但是,正如您可以想象的那样,编译器甚至没有机会在问题产生之前处理任何事情。 C 或 C++ 不支持循环依赖。

更现代的编译器,例如 JavaC#,能够提取类的公共接口,并在需要 A 类时使用此信息B 类,反之亦然。同样,这在 C++ 中是不可能的。

唯一的解决方案是重写您的代码。目标是消除循环 #include 指令。实际上,如果您的代码像您在此处显示的那样简单,您可以通过使用前向声明轻松解决它:

// foo.h
class bar;

class foo {
  foo();
  bar& bind(bar &b);
  gru& bind(gru &g);
};

--

// bar.h
class foo;

class bar {
  bar();
  foo& bind(foo &f);
  gru& bind(gru &g);
};

--

// main.cpp
#include "foo.h"
#include "bar.h"

// ...
int main()
{
    // ...
}

前向声明可以让编译器知道该类实际上将存在,并且您正在提前声明它的存在,但不提供有关其内容的详细信息。这就是为什么你可以只在另一个类中使用这个前向声明作为指针(或引用)(编译器需要前向类的大小才能创建属性,如果没有细节这是不可能的)。

这里还有其他概念可以发挥作用,例如编译器保护。

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    非常感谢大家的回答。他们在很多方面都提供了帮助。

    最后我意识到我必须对代码中的所有#include 重新排序,因为正如您可能已经意识到的那样,还有更多的编码,而我放在这里的是一个更简单的版本(抱歉那个)。

    所以,我的最终解决方案是将class bar; 包含在foo.h 中,并将class foo; 包含在bar.h 中。然后重新排序 main.cpp 和 Makefile 中的包含。

    再次感谢 ;-)

    【讨论】:

      【解决方案3】:

      这对我来说编译得很好(注意:这没有实例化 foo 或 bar 类):

      文件 bar.h 包含:

      #ifndef BAR_H
      #define BAR_H
      
      #include "foo.h"
      
      class foo;
      class gru;
      
      class bar {
      
        bar();
        foo& bind(foo &f);
        gru& bind(gru &g);
      };
      
      #endif
      

      文件 foo.h 包含:

      #ifndef FOO_H
      #define FOO_H
      #include "bar.h"
      class bar;
      class gru;
      class foo {
        foo();
        bar& bind(bar &b);
        gru& bind(gru &g);
      };
      
      #endif
      

      主 .cpp 文件包含:

      #include "foo.h"
      #include "bar.h"
      

      【讨论】:

        【解决方案4】:

        在 foo.h 中

          #pragma once //(or use a guard symbol)
          class bar;
          class foo
          {
             bar & bind(bar & b);
          };
        

        在 bar.h 中

          #pragma once //(or use a guard symbol)
          class foo;
          class bar
          {
             foo & bind(foo & f);
          };
        

        在 foo.cpp 中

        #include <foo.h>
        #include <bar.h>
        
        bar foo:bind(bar & b)
        {
          // here you can use bar methods
        }
        

        在 bar.cpp 中

        #include <bar.h>
        #include <foo.h>
        
        foo bar:bind(foo & f)
        {
          // here you can use foo methods
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-06-25
          • 1970-01-01
          • 2013-04-05
          • 1970-01-01
          • 2019-11-25
          • 2012-05-20
          • 1970-01-01
          相关资源
          最近更新 更多