Пытаюсь решить некоторую фундаментальную проблему (проблема существует в локальном контексте особых проектов), а именно связывание по родительской линии различных объектов. Все мы помним интересные, а кто-то считает это извратом, исследования и разработки товарища Александреску, многие уже оценили стратегическое проектирование с использованием шаблонов и примеры использующие такой подход - Loki::SmartPtr. Все это навеяло на некоторые дурные идеи. Итак, есть базовый класс строящийся из политик Policy, этот базовый класс наследуется всеми классами более высокого уровня, тем самым каждый объект имеет доступ к базовым возможностям заложенным в этот фундаментальный класс. Это дает возможность обращаться к базовым фичам любого класса, отлично подходит для реализации сервисного функционала. Одна из политик - Family, подразумевает связыванием объектов по родительской линии. Например у нас имеется объект A, объект B1 и объект B2. Объект A является родительским, объекты B1, B2 дочерними. Все объекты унаследованы от фундаментального класса. Задачи политики Family - обеспечить доступ к фундаментальному классу объектов B1, B2 через объект A. Псевдокод: Код (Text): APtr pa = new A(); if (pa != nullptr) { for (child_t &i: pa->base.family.childs) // рассматриваем фундамент объектов B1,B2 через итератор как дочерние элементы, про существование B1 и B2 мы можем ничего не знать { .... } } Задача вполне решаемая, если связывать объекты вручную, но этот вариант не подходит по концепции, которая выглядит следующим образом: * Фундаментальный класс рассматривается как примитив - легкий и быстрый * Фундаментальный класс должен быть самостоятельным * Классы более высокого уровня унаследовавшие фундаментальный класс не должны изменяться во внутренней логике * Все вышеописанное позволяет создать дополнительный уровень контроля и принятия решений в работе логики классов Единственный, пришедший в голову, вариант для связи выглядит следующим образом: Конструктору дочерних классов передается this родительского (тоже не самое лучшее решение, т.к. RAII подход может обрушить конструктор после добавления нового класса в список родителя. Либо аналогичное решение в виде вызова функции setParent(this) у дочерних элементов сразу после конструирования. Все это требует модификации кода уже существующих классов. Одно дело в хидере добавить наследование, другое дело лезть в код. Все это напоминает собой .NET, там это используется для GC. Вопрос такой: как произвести автоматическое легкое связывание (без модификации кода связываемых классов) по схеме родители<->дети. Допускаются любые идеи, хаки, фичи компиляторов, главное чтобы оно как-то заработало. Также вспомнился Phoenix Project, но увы под VS2010 оно не работает и проект вообще давно не развивается, но может есть какие другие, о которых я не знаю?
Ничего не понял. this дочерних объектов неявно приводится к родительскому (кроме случаев ромбовидного наследования). Зачем его где-то хранить? Или речь не про наследование типов, а про агрегирование ссылок на другие объекты?
im1111 Ну тогда все зависит от того, кто контролирует время жизни дочерних объектов, и как этот контроль связан с временем жизни родителя. В зависимости от ситуации это может быть контейнер shared_ptr, или контейнер простых поинтеров. Или что-то еще.
Ммм, забыл ньюанс - фундаментальный класс создает "контекст" (обычный класс), который построен через SmartPtr со счетчиком ссылок, в этом контексте есть ссылка на сам класс. Манипуляции происходят с этим контекстом, т.е. если объект разрушился, то невалидной будет ссылка в контексте на этот объект (если контекст еще используется и не разрушен). Получается что мы связываем между собой контексты, через которые мы можем получить доступ к объекту, которому принадлежит контекст (проксирование).
im1111 Ну опять же, тогда все зависит от того, как соотносятся времена жизни "фундаментального класса" и "контекста". Можно хранить shared_ptr на фундаментальный класс внутри контекста - тогда первый будет жить как минимум до тех пор, пока жив второй.
ага, со временем жизни уже решено, это я упростил все, чтобы не запутывать сложностями реализации, вопрос собственно в автоматическом связывании
im1111 Ну например так: контекст создается методом фундаментального класса. Контекст через конструктор принимает shared_ptr. Фундаментальный класс отнаследован от enable_shared_from_this чтобы передавать shared_ptr на себя внутри метода и не напрягать юзера: Код (Text): class fundamental : public std::enable_shared_from_this<fundamental> { public: std::shared_ptr<context> make_context(...) { return std::make_shared<context>(shared_from_this()); } };
Если дочерний объект нельзя модифицировать, то как он будет использовать родительский функционал? А если его будут использовать извне, то заводите контейнер для связей и все дела. З.Ы. И прошу, не пишите "хидер".
Дочерний объект модифицировать можно, имеется в виду модификация его описания в ".h" файле, т.е.: Код (Text): было: class A { .... } стало: class A: public base { ... } Это допустимо, т.к. можно модифицировать все .h, а этот base подменить заглушкой если отпадет необходимость использования в будущем. Пример: Код (Text): class B: public base { .... } class A: public base { .... private: SmartPtr<B> m_lpB; } Нужно уведомить m_lpB о том что его родитель class A. Я описал выше, что в голову приходит только: Код (Text): m_lpB = SmartPtr(new B(this)); в коде класса A и это не лучшее решение, т.к. все конструкторы классов придется модифицировать, что не есть хорошо. Ну еще дикий метод - препроцессор который все сделает сам перед компиляцией, однако это целый парсер C++ кода придется писать, готовых решений я не видел.
Если можно менять заголовочный файл и при этом нельзя трогать .cpp, то можно сделать следующий хак. Base.h Код (Text): #pragma once template<typename T> class Base { public: Base() { T *p = static_cast<T*>(this); p->m_lpB = SmartPtr(new B(this)); } }; Derived.h Код (Text): #pragma once #include "Base.h" class Derived : Base<Derived> { friend Base<Derived>; private: SmartPtr<B> m_lpB; };