【问题标题】:C++ Tricky Inheritance Class Definition ProblemC++ 棘手的继承类定义问题
【发布时间】:2009-04-12 08:15:45
【问题描述】:

在处理包括彼此在内的多个类时出现此错误:

error: expected class-name before '{' token

我知道发生了什么,但我不知道如何正确纠正它。这是代码的抽象版本:

啊.h

#ifndef A_H_
#define A_H_

#include "K.h"

class A
{
    public:
        A();

};

#endif /*A_H_*/

A.cpp

#include "A.h"

A::A() {}

B.h

#ifndef B_H_
#define B_H_

#include "A.h"

class B : public A
{ // error: expected class-name before '{' token
    public:
        B();
};

#endif /*B_H_*/

B.cpp

#include "B.h"

B::B() : A() {}

J.h

#ifndef J_H_
#define J_H_

#include "B.h"

class J
{
    public:
        J();
};

#endif /*J_H_*/

J.cpp

#include "J.h"

J::J() {}

K.h

#ifndef K_H_
#define K_H_

#include "J.h"

class K : public J
{ // error: expected class-name before '{' token
    public:
        K();
};

#endif /*K_H_*/

K.cpp

#include "K.h"

K::K() : J() {}

main.cpp

#include "A.h"

int main()
{
    return 0;
}

main.cpp开始,我可以确定这是编译器看到的:

#include "A.h"

#ifndef A_H_
#define A_H_

#include "K.h"

#ifndef K_H_
#define K_H_

#include "J.h"

#ifndef J_H_
#define J_H_

#include "B.h"

#ifndef B_H_
#define B_H_

#include "A.h"

class B : public A
{ // error: expected class-name before '{' token

所以,当我们到达B时,A的定义并不完整。有人告诉我,有时您需要使用前向声明,然后将 #include 语句移动到 .cpp 文件中,但我没有任何运气那。如果我尝试这样的事情,我只会得到额外的错误:

error: forward declaration of 'struct ClassName'

我想也许我只是没有在正确的地方做事。有人可以告诉我如何编译这段代码吗?非常感谢!


编辑:我想指出这只是真实代码的抽象版本。我意识到在 A 中没有对 K 的引用,在 J 中没有对 B 的引用,但在真正的代码,我觉得它们是完全必要的。也许如果我对真实类进行简要描述,有人可以帮助我重组或修复我的代码。

Class A 是一个抽象节点类,充当图中节点的接口。 B 类是 A 的许多不同实现之一。同理,类J是一个抽象的Visitor类,K是对应的实现。这是具有更多上下文的代码:

A.h(抽象节点)

#ifndef A_H_
#define A_H_

#include "K.h"

class K;

class A
{
    public:
        A();

        virtual void accept(const K&) const = 0;
};

#endif /*A_H_*/

A.cpp

#include "A.h"

A::A() {}

B.h(具体节点)

#ifndef B_H_
#define B_H_

#include "A.h"

class K;

class B : public A
{ // error: expected class-name before '{' token
    public:
        B();

        virtual void accept(const K&) const;
};

#endif /*B_H_*/

B.cpp

#include "B.h"

B::B() : A() {}

void B::accept(const K& k) const { k.visit(this); }

J.h(抽象访问者)

#ifndef J_H_
#define J_H_

#include "B.h"

class B;

class J
{
    public:
        J();

        virtual void visit(const B*) const = 0;
};

#endif /*J_H_*/

J.cpp

#include "J.h"

J::J() {}

K.h(具体访客)

#ifndef K_H_
#define K_H_

#include "J.h"

class B;

class K : public J
{ // error: expected class-name before '{' token
    public:
        K();

        virtual void visit(const B*) const;
};

#endif /*K_H_*/

K.cpp

#include "K.h"

K::K() : J() {}

void K::visit(const B*) const {};

main.cpp

#include "A.h"

int main()
{
    return 0;
}

我必须添加一些前向声明以消除出现的一些额外错误(当我添加细节时)。其中一些可能不是必需的或不正确的。

【问题讨论】:

    标签: c++ inheritance forward-declaration


    【解决方案1】:

    您的标题包含是循环的。 A -> K -> J -> B -> A。使用前向声明是避免这种复杂性的最简单方法,但只有在被声明的类仅使用引用或指向所包含类的指针时才有可能。例如,您不能在 K.h 或 B.h 中使用前向声明,因为它们分别继承自 J 和 A。但是,您可以将 A.h 中的 #include "K.h" 替换为 class K;,具体取决于您在 A 中实际使用 K 的方式。

    谈论困惑。

    【讨论】:

    • 我试过这个,我得到了“'struct K'的前向声明”错误。我在我的问题中添加了更多细节,希望您能重新评估它。
    • 我注意到在您的第一个示例中,您同时拥有 #include "K.h" 和类 K;线。您是否尝试完全删除 #include 行,或者这只是您帖子中的疏忽?
    • 我不明白。在我的第一个例子中?我在 A 类中包含 K,但没有前向声明。我试着像你说的那样删除“#include”并添加一个前向声明,但正如我所说,我得到了错误:“'struct K'的前向声明。”
    • 对不起,我的意思是在您更新示例的第一个 sn-p 中。标记为“A.h(抽象节点)”的那个。您在该 sn-p 中有一个 #include 和一个前向声明。
    • 啊,是的,这很奇怪。我知道当一切都编译好时,我可能不需要它。然而,奇怪的是,当我不转发声明类 K 时,在具有“接受”方法声明的行上,我得到:“错误:ISO C++ 禁止声明没有类型的 'K'。”
    【解决方案2】:

    在这个具体的例子中,删除

    #include "B.h"
    

    来自文件J.h,因为那里不需要它。如果这不起作用,我们需要更多关于 J 如何使用 B 的详细信息...

    编辑

    由于J 仅使用指向B 的指针,因此建议保持不变:-) :

    J.h 中删除#include "B.h" 并将其替换为B 的前向声明:

    (J.h)
    class B;
    
    class J
    {
      // ...
      virtual void visit(const B*) const = 0; // This will work with forward declaration
    }
    

    同时从A.h 中删除#include "K.h"

    重要

    当然,您需要在相应的 CPP 文件中添加所需的包含:

    (J.cpp)
    #include "B.h"
    // ...
    // Go ahead with implementation of J
    // ...
    

    A.cpp 相同,包括 K.h

    【讨论】:

    • 我为我的问题添加了一些上下文。我希望它有助于解释我正在尝试做的事情。
    【解决方案3】:

    主要问题是您的头文件以循环方式相互包含。 A 包括 K,其中包括 J,其中包括 B,然后又包括 A……您永远不应该要求发生这种情况的情况。你应该回到设计图板,要么削减一些依赖,要么完全重新组织你的类,这样这种循环依赖就不会发生。

    【讨论】:

    • 你能看看我更新的问题吗?我添加了更多细节,所以也许你可以帮助我以某种方式重组代码。
    【解决方案4】:

    问题在于您的头文件周期性地相互依赖。不要在A.h 中包含K.h,在J.h 中包含B.h。那里不需要它们。

    关于您的编辑:这不是您的对象交互的最佳方式。重新考虑您的设计。谁打电话给node.accept(visitor)?不能直接打visitor(node)吗?

    另外,如果您正在尝试设计图形库,请查看Boost.Graph library。

    也就是说,由于您只在J.h 中使用指向B 的指针,因此您不必包含B.h,只需前向声明该类。

    class B;
    
    struct J
    {
        virtual void visit(const B*) const = 0;
    };
    

    然后将B.h 包含在您的K.cpp 文件中。

    #include "K.h"
    #include "B.h"
    
    void K::visit(const B* b) const
    {
        // use b here any way you want
    }
    

    【讨论】:

    • 如果我的例子有误导性,我很抱歉,但在代码的真实版本中,包含是必要的。我已经编辑了我的问题,所以也许你可以再看看它。
    【解决方案5】:

    圆形夹杂物不起作用。

    尽量将夹杂物控制在最低限度。如果你这样做,你要么完全没问题,要么你会发现设计中的问题。就您而言,我认为您的设计没有任何问题。

    定义类 K 时,您只使用指向 B 类型对象的指针。这不需要定义 B(如“包含头文件”),只需声明(前向声明即可)。因此,在您的情况下,将包含到标题“B.h”中的内容删除为“B 类;”足够了。 (J班也一样)

    【讨论】:

      猜你喜欢
      • 2018-07-07
      • 2011-01-30
      • 1970-01-01
      • 2012-05-11
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多