Привет всем! На днях столкнулся с одной проблемой программирования на С++... Вот ситуация:
допустим есть два класса, находящиеся соответственно в фалах headerone.h и headertwo.h, и у каждого объявлена переменная другого:
Ну, самое простое, сделай базисный класс MyBasicClass
и наследуй MyClassOne и MyClassTwo из него. Потом делай приватные указатели на базисный класс. Типа вот так
class MyClassOne : public MyBasicClass
{
public:
void SomeFunction();
private:
MyBasicClass* objPtr;
}
Привет всем! На днях столкнулся с одной проблемой программирования на С++... Вот ситуация:
допустим есть два класса, находящиеся соответственно в фалах headerone.h и headertwo.h
Короче, ситуация такая... Пишу игровой движок, есть класс Root и в нем приватная переменная Scene* cScene. Потом в другом заголовочном файле вставляется заголовок Root-а, и там в качестве аргумента для конструктора есть указатель на Root. То есть получается рекурсивное объявление... Сами понимаете, scene и root совсем разные объекты, так что наследование тут будет неэффективным
То что предлагает v04bvs - классическое решение проблемы. Если в классе используются только указатели - то руководства С++ и здравый смысл настойчиво рекомендуют forward declaration в хидере класса с последующим подключением необходимых хидеров в коде.
VoidExp, для Вашей ситуации может оказаться полезным объявление класса Root синглтоном - не потребуется тащить его через все конструкторы.
Fire and water, earth and sky - mistery surrounds us, legends never die!
Возникло недоразумение: выше приведенное решение почему-то не работает, вроде работает вот это:
А чем оно отличается от приведенного выше?
После
#include "scene.h"
уже лишнее
class Scene;
Объявление структуры-класса нужно когда
- надо знать ее размер
- обращаться к ее членам
Размер указателя известен и так. Поэтому для указателя дефинишн структуры не нужен, достаточно указать компилятору что эта "Блабла" - имя типа. Отсюда class Blah.
В срр файлы (где обыкновенно уже нужен доступ к членам...) включаем хедеры полностью.
В языке Java практикуется использование описание интерфейса - interface. В C++ это класс с абстрактными функциями (методами).
Вы создаете для каждого класса свой интерфейс. Интерфейс не имеет параметров и реализаций методов.
В вашем случае, для Root создаём итерфейс IRoot с методом initScene(IScene* pScene).
Для ответного класса Scene создаем интрфейс IScene с методом initRoot(IRoot* pRoot).
Тем самым вы отвязываете жесткую зависимость классов друг от друга.
Поле этого смотрим коментарий sergio, или читаем кондуит по C++.
Ну, это уже дело вкуса и подходов к программированию. По моему это имеет смысл для огромных (вроде Qt) проектов, где используется массивные реализации интерфесов, шаблонов и т.п. Это делает библиотеку громоздкой, мне же хочется реализовать движок с чистой и понятной структурой. Можете объяснить пожалуйста, почему в каждом файле должно быть взаимное объявления классов?
То есть в файле Root.h так:
namespace zEngine
{
class Root;
class Scene {//interface}/
}
Еще один вопрос (я думаю что вы довольно-таки неплохо разбираетесь в С++ ): возможно-ли объявить Root и Scene вне пространства имен? То есть так: class zEngine::Root & class zEngine::Scene? Я пробовал, но g++ ругается... Не знаю для чего это понадобится в будущем, но все-таки желательно иметь предствление =)
VoidExp,
>хочется реализовать движок с чистой и понятной структурой.
как раз концепция интерфейсов и обеспечивает чистую и понятную структуру. Вместо того чтобы явно указывать класс в качестве параметра функции, указывается интерфейс. Интерфейс описывает минимально необходимую в данном случае функциональность (например интерфейс элемента сцены для рендера описывает только положение элемента и его перемещения, а интерфейс физики - только действие сил). Реальный же объект имеет право реализовать сколь угодно много интерфейсов (т.е. произвольный игровой объект может реализовать интерфейс физики и положения одновременно). Это обеспечивает "маскировку" объекта под требуемую функциональность.
>Можете объяснить пожалуйста, почему в каждом файле должно быть взаимное объявления классов?
при компиляции программы компилятору необходимо знать, какие типы данных используются в каждой единице трансляции. Без этого постоянно будут проявляться ошибки - тип данных ХХХ не определен. Если подключать хидер в хидере, токомпилятору становятся доступны все параметры включаемой единицы трансляции. Если же (как в Вашем случае) в хидере элемента указаны только указатели на другие элементы - компилятору достаточно знать только о наличии таких типов объектов - в каком namespace они объявлены. В этом случае гораздо эффективнее будет forward declaration.
>возможно-ли объявить Root и Scene вне пространства имен? То есть так: class zEngine::Root & class zEngine::Scene
если я правильно понял вопрос - то нет. правильный forward declaration будет всегда содержать объявление всех необходимых namespaces со всеми требуемыми классами. запись namespace::Class в forward declaration в хидере недопустима.
Fire and water, earth and sky - mistery surrounds us, legends never die!
возможно-ли объявить Root и Scene вне пространства имен? То есть так: class zEngine::Root & class zEngine::Scene? Я пробовал, но g++ ругается... Не знаю для чего это понадобится в будущем, но все-таки желательно иметь предствление =)
Нельзя объявить просто
some_namespace::MyClass ;
Потому что следующий вопрос компилятора будет "а что такое это слово "some_namespace","
Сперва надо объявить нэймспейс.
Члены нэймспейса должны быть объявлены внутри нэймспейса (точнее - видимой его части; нэймспейс можен быть размазан по сотне хедеров, как std).
Отсюда имеем: