Разделение связанных классов на отдельные заголовки - PullRequest
0 голосов
/ 26 августа 2018

Я пытаюсь разделить следующие два класса, чтобы каждый класс мог быть определен в своем собственном заголовке:


#include <iostream>

class Boo;
class Foo
{
public:
    Foo(Boo *booPtr)
    :booPtr(booPtr){};
    virtual ~Foo(){};
    Boo *booPtr;
};

class Boo
{
public:
    Boo()
    :foo(this){};
    virtual ~Boo(){};
    Foo foo;
    int num = 32;
};

int main()
{
    Boo *boo = new Boo;
    std::cout << "booPtr : " << boo->foo.booPtr->num << '\n';
    delete boo;
}

Результат:

booPtr : 32
Program ended with exit code: 0

А вот моя неудачная попытка отделения:

"Foo.hpp"

#ifndef Foo_hpp
#define Foo_hpp

#include <stdio.h>
#include "Boo.hpp"

class Foo
{
public:
    Foo(Boo *booPtr)
    :booPtr(booPtr){};
    virtual ~Foo(){};
    Boo *booPtr;
};

#endif /* Foo_hpp */

"Boo.hpp"

#ifndef Boo_hpp
#define Boo_hpp

#include <stdio.h>
#include "Foo.hpp"

class Boo
{
public:
    Boo()
    :foo(this){};
    virtual ~Boo(){};
    Foo foo; // Error : Field has incomplete type 'Boo'
    int num = 32;
};

#endif /* Boo_hpp */

"main.cpp"

#include <iostream>
#include "Foo.hpp"
#include "Boo.hpp"

int main()
{
    Boo *boo = new Boo;
    std::cout << "booPtr : " << boo->foo.booPtr->num << '\n';
    delete boo;
}

Но я не могу собрать код, так как он генерирует следующую ошибку:

Boo.hpp -> Foo foo; -> "Поле имеет неполный тип 'Boo'"

Как я могу исправить свой код?

Ответы [ 3 ]

0 голосов
/ 26 августа 2018

Несмотря на то, что само по себе циклическое включение, как предполагалось, включает в себя надлежащие защитные ключи, это не ошибка, помните, что это просто текстовая замена:

Убедитесь, что все перестановки жизнеспособны и имеют одинаковое значение!

В вашем случае замените включение "Boo.hpp" на предварительное объявление, которое вы по какой-то причине использовали в объединенном источнике.

Или, вы знаете, просто отказаться от разделения заголовка:
Помните, что классы не обязательно являются правильной единицей организации.

0 голосов
/ 26 августа 2018

Добавление к циклу включает, начиная с Foo.hpp:

#ifndef FOO
#define FOO  // (!)

// now including BOO!
#ifndef BOO
#define BOO

// now including FOO  a g a i n
// as FOO  i s  already defined, all that remains is:
#ifndef FOO
#endif

class Boo { }; // now lacking definition of Foo...

#endif // BOO

class Foo { };

#endif // FOO (first one)

Аналогично Boo.hpp (вам потребуется только предварительное объявление Foo, но его нет, так как определение снова следуетпотом ...).

Как видите, циркулярные включения, включая соответствующие охранники включения, не приведут к бесконечному само включению.Однако результат не тот, который вы рассчитывали.

0 голосов
/ 26 августа 2018

У вас есть один файл, который включает в себя другой, и наоборот. Это создает цикл включения.

Поймите, что макрос #include не делает ничего, кроме как заменить себя (то есть, это строка) другим файлом. Это делает очевидным, почему у вас не может быть файла A, включающего файл B.

Очевидное решение - поместить предварительное объявление Boo в Foo:

#ifndef Foo_hpp
#define Foo_hpp

#include <stdio.h>
class Boo;

class Foo
{
public:
...

Странно, вы уже сделали это до того, как разделили их.

Теперь немного больше теории для вас: класс технически является хранилищем данных. Он должен знать его размер, чтобы зарезервировать память. Следовательно, ему нужно знать все размеры его членов, которые он может знать только тогда, когда они объявлены. Поэтому класс должен включать объявления заголовков каждого класса, который он имеет в качестве члена. Тем не менее, указатель на объект отличается (то же самое касается ссылок). Указатель всегда принимает один и тот же размер, то есть 32 или 64 бита, в зависимости от вашей платформы (вероятно, 64-битной, поскольку в настоящее время у нас есть 64-битные платформы). Следовательно, классу не нужно знать класс, на который он указывает, память, которую он резервирует для своего элемента указателя, всегда имеет одинаковый размер. Вот почему предварительное объявление, которое ничего не говорит о размере классов, здесь хорошо.

...