Сложная проблема определения класса наследования в C++

StackOverflow https://stackoverflow.com/questions/741452

Вопрос

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

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

Б.х.

#ifndef B_H_
#define B_H_

#include "A.h"

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

#endif /*B_H_*/

Б.cpp

#include "B.h"

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

Дж.Х.

#ifndef J_H_
#define J_H_

#include "B.h"

class J
{
    public:
        J();
};

#endif /*J_H_*/

J.cpp

#include "J.h"

J::J() {}

К.х.

#ifndef K_H_
#define K_H_

#include "J.h"

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

#endif /*K_H_*/

К.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

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

error: forward declaration of 'struct ClassName'

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


Редактировать:Хочу отметить, что это всего лишь абстрактная версия реального кода.Я понимаю, что нет никаких ссылок на К в А или Б в Дж, но они есть в реальном коде, и я считаю, что они совершенно необходимы.Возможно, если я дам краткое описание реальных классов, кто-то поможет мне реструктурировать или исправить мой код.

Сорт А — это абстрактный класс узла, который действует как интерфейс для узлов в графе.Сорт Б это одна из множества различных реализаций А.Таким же образом класс Дж представляет собой абстрактный класс Посетителя и К это соответствующая реализация.Вот код с немного большим контекстом:

А.х. (Абстрактный узел)

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

Б.х. (Бетонный узел)

#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_*/

Б.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_*/

К.cpp

#include "K.h"

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

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

main.cpp

#include "A.h"

int main()
{
    return 0;
}

Мне пришлось добавить несколько предварительных объявлений, чтобы устранить некоторые дополнительные ошибки, которые появлялись (когда я добавлял детали).Некоторые из них могут оказаться ненужными или неправильными.

Это было полезно?

Решение

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

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

При определении класса K вы используете только указатель на объект типа B.Для этого не требуется, чтобы B был определен (как в случае «включить файл заголовка»), а только для объявления (форвардное объявление подойдет).Итак, в вашем случае удаление включения в заголовок «BH» заменен на «класс B»; достаточно.(то же самое касается класса J)

Другие советы

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

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

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

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

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

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

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

Кроме того, если вы пытаетесь создать библиотеку графов, взгляните на Boost.График библиотека.

Тем не менее, поскольку вы используете только указатель на 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
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top