Сложная проблема определения класса наследования C ++ - PullRequest
1 голос
/ 12 апреля 2009

Я получаю эту ошибку при работе с рядом классов, включая друг друга:

error: expected class-name before '{' token

Я вижу, что происходит, но я не знаю, как правильно это исправить. Вот абстрактная версия кода:

хиджры

#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

Итак, определение A не является полным, когда мы достигаем B . Мне сказали, что иногда вам нужно использовать предварительное объявление, а затем переместить оператор # include в файл .cpp , но мне не повезло с этим. Если я попробую что-нибудь подобное, я просто получу дополнительную ошибку:

error: forward declaration of 'struct ClassName'

Я думаю, может быть, я просто не делаю вещи в нужных местах. Может кто-нибудь показать мне, как получить этот код для компиляции? Большое спасибо!


Edit: я хочу отметить, что это просто абстрактная версия реального кода. Я понимаю, что нет никаких ссылок на K в A или B в J , но есть в реальном коде, и я чувствую что они абсолютно необходимы. Возможно, если я дам краткое описание реальных классов, кто-то может помочь мне реструктурировать или исправить мой код.

Класс 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); }

Дж.ч (Абстрактный посетитель)

#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() {}

Кч (Конкретный посетитель)

#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 ]

6 голосов
/ 12 апреля 2009

Включения вашего заголовка круглые. A -> K -> J -> B -> A. Использование предварительных объявлений - самый простой способ избежать такой сложности, но это возможно только тогда, когда объявляемый класс использует только ссылки или указатели на включаемый класс. Вы не можете использовать прямое объявление в K.h или B.h, например, потому что они наследуются от J и A соответственно. Однако вы можете заменить #include "K.h" в A.h на class K; в зависимости от того, как вы на самом деле используете K в A.

Поговорим о путанице.

3 голосов
/ 12 апреля 2009

В этом конкретном примере удалите

#include "B.h"

из файла J.h, поскольку он там не нужен. Если это не сработает, нам нужно больше подробностей о том, как J использует B ...

EDIT

Поскольку 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 голосов
/ 12 апреля 2009

Основная проблема заключается в том, что ваши заголовочные файлы включают друг друга по кругу. A включает в себя K, который включает в себя J, который включает в себя B, который затем включает в себя A снова ... Вы никогда не должны требовать ситуации, когда это происходит. Вы должны вернуться к чертежной доске дизайна и либо обрезать некоторые зависимости, либо полностью реорганизовать свои классы, чтобы не возникала эта круговая зависимость.

2 голосов
/ 12 апреля 2009

Проблема в том, что ваши заголовочные файлы циклически зависят друг от друга. Не включайте K.h в A.h и B.h в J.h. Они там не нужны.

Относительно вашего редактирования: это не лучший способ взаимодействия ваших объектов. Переосмыслите свой дизайн. Кто звонит node.accept(visitor)? Вы не можете позвонить visitor(node) напрямую?

Также, если вы пытаетесь спроектировать библиотеку графов, взгляните на библиотеку Boost.Graph .

Тем не менее, поскольку вы используете только указатель на B в J.h, вам не нужно включать 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
}
1 голос
/ 12 апреля 2009

Круговые включения не работают.

Постарайтесь свести включения к строгому минимуму. Если вы сделаете это, у вас все будет хорошо, или вы обнаружите проблемы в своем дизайне. В твоем случае я не вижу ничего плохого в твоем дизайне.

При определении класса K вы используете только указатель на объект типа B. Для этого не требуется определять B (как в «include the header file»), только объявлять (предварительное объявление хорошо ). Итак, в вашем случае удаление включения в заголовок "B.h" заменено на "class B;" достаточно. (то же самое относится и к классу J)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...