Я пытаюсь создать решение, которое имеет три файла. С main.cpp это четыре файла.
Entity.h
#pragma once
#include "SystemBase.h"
namespace Engine {
class Entity {
public:
Entity() { }
void s(SystemBase* sb) { }
};
}
SubscribersList.h
#pragma once
#include "SystemBase.h"#include "Entity.h"
namespace Engine {
class SubscribersList {
friend SystemBase;
public:
SubscribersList() { }
void f(Entity* e) { }
};
}
SystemBase.h
#pragma once
#include "SubscribersList.h"#include "Entity.h"
namespace Engine {
class SystemBase {
public:
SystemBase() { }
void g(Entity* e) { }
private:
SubscribersList m;
};
}
Не сосредотачивайтесь на теле методов в заголовках. Это просто для простоты. Я нашел два способа создать решение.
1. Напишите слово класс перед всеми именами классов. Но происходит сбой, когда я пытаюсь отделить реализацию от прототипов.
2. Запишите весь код в одном файле.
Я не / не буду писать ключевое слово class перед всеми именами классов для построения решения, и, конечно, я не буду / не буду писать большой проект в одном файле. Так почему я не могу это построить? Что за магия ?!
Чтобы понять проблему зависимости циклического заголовка, нам сначала нужно понять разницу между объявлением и определением класса и концепцией неполных типов.
Прототип или предварительное объявление типа Type
записывается как:
class Type;
Такое предварительное объявление позволяет создавать указатели и ссылки на этот тип.
Однако вы не можете создавать экземпляры, разыменовывать указатели или использовать ссылку на Type
пока его полный тип не объявлен.
Декларация для Type
может быть написано как:
class AnotherType;
class Type {
public:
void aMemberFunc();
private:
AnotherType *m_theOtherThing;
};
Теперь у нас есть экземпляры объявлений, которые могут быть созданы, и указатели на Type
может быть разыменовано.
Однако раньше m_theOtherThing
разыменовывается или создается AnotherType
должны быть полностью объявлены.
class AnotherType {
Type m_aType;
}
Должен сделать, что дает нам как полную декларацию и определение AnotherType
,
Это позволяет продолжить писать определение Type::aMemberFunc
:
void Type::aMemberFunc() {
m_theOtherThing = new AnotherType();
}
Если вместо того, чтобы представить этот код компилятору в этом порядке, мы вместо этого представили полные объявления Type
а также AnotherType
впереди:
class Type {
public:
void aMemberFunc();
private:
AnotherType *m_theOtherThing;
};
class AnotherType {
Type m_aType;
}
затем AnotherType *m_theOtherThing;
не скомпилируется как AnotherType
не был объявлен или форвард объявлен этим пунктом.
Переключение заказа дает:
class AnotherType {
Type m_aType;
}
class Type {
public:
void aMemberFunc();
private:
AnotherType *m_theOtherThing;
};
Сейчас Type m_aType;
не будет компилироваться как Type
не был объявлен. Предварительная декларация не подойдет в этом случае.
С помощью #pragma once
вместо охранников заголовка ни в коем случае не меняет проблему. #pragma once
только гарантирует, что заголовок будет включен, только если он не влияет на порядок, которым компилятор обрабатывает код в противном случае. Это, конечно, не позволяет компилятору игнорировать неопределенные типы, когда он достигает их.
Для такого рода структуры класса компилятор не сможет обработать его без использования для предварительных объявлений.
Других решений пока нет …