C ++棘手的继承类定义问题

时间:2009-04-12 08:15:45

标签: c++ inheritance forward-declaration

在处理包括彼此在内的多个类时,我遇到了这个错误:

error: expected class-name before '{' token

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

A.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'

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


编辑:我想指出这只是真实代码的抽象版本。我意识到在 J 中的 A B 中没有对 K 的引用,但实际上有代码,我觉得他们是完全必要的。也许如果我简要介绍一下真正的类,有人可以帮我重构或修改我的代码。

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

我不得不添加一些前向声明来使一些额外的错误(当我添加细节时)消失。其中一些可能没有必要或不正确。

5 个答案:

答案 0 :(得分:6)

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

谈论混乱。

答案 1 :(得分:3)

在此具体示例中,删除

#include "B.h"

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

的详细信息

修改

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

#include "B.h"移除J.h并将其替换为B的转发声明:

(J.h)
class B;

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

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

重要

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

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

A.cpp相同,包括K.h

答案 2 :(得分:2)

问题是您的标头文件周期性地相互依赖。请勿在{{1​​}}中加K.h,在A.hB.h。那里不需要它们。

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

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

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

B.h

然后在class B; struct J { virtual void visit(const B*) const = 0; }; 文件中添加B.h

K.cpp

答案 3 :(得分:2)

主要问题是您的头文件以循环方式相互包含。 A包括K,其中包括J,其中包括B,然后再包括A ......你永远不应该要求发生这种情况。您应该回到设计绘图板并切断一些依赖项,或者完全重组您的类,以便不会发生这种循环依赖。

答案 4 :(得分:1)

圆形内含物不起作用。

尽量将夹杂物保持在最低限度。如果你这样做,你可以完全没问题,或者你会发现你的设计出现问题。在您的情况下,我认为您的设计没有任何问题。

当定义类K时,你只使用指向类型B的对象的指针。这不需要定义B(如“包含头文件”),只是要声明(前向声明很好) )。因此,在您的情况下,删除包含标题“B.h”替换为“B类”;足够了。 (同样适用于J级)