asker
|
|
« : 19-02-2007 08:51 » |
|
Есть шаблон синглтона с контролем числа ссылок: template <class T> class Singleton {...}
Есть класс CProtocol, который является интерфейсом: Есть класс CFProtocol : public CProtocol. Класс CFProtocol - должен быть и синглтоном. Т.е. в системе может быть несколько классов, подобных CFProtocol (т.е. соответствующих интерфейсу CProtocol), но при этом для каждого такого класса можно создать только один экземпляр данного класса. Делать CProtocol наследником Singleton нельзя, т.к. CProtocol абстрактный класс, т.е. вот так: class CProtocol : public Singleton<CProtocol> {...};
Применять множественное наследование тоже не очень хочется, т.к. нет контроля кода для CFProtocol. Встраивать реализацию синглтона в CProtocol кажется неправильным. Пробовал делать CProtocol шаблоном, т.е. так: template <class Protocol> class CProtocol : public Singleton<Protocol> {...};
... class CFProtocol : public CProtocol<Protocol> {...};
Но компилятор ругается и говорит ошибку C2989, т.е. класс Cprotocol уже описан. Подскажите пожалуйста.
|
|
|
Записан
|
С уважением, asker
|
|
|
sss
Специалист
Offline
|
|
« Ответ #1 : 19-02-2007 09:25 » |
|
Не понял, что делает класс Singleton? Контролирует количество ссылок на объект класса T? Тогда, если CFProtocol должен являться наследником Singleton, он должен быть наследником класса Singleton<CFProtocol>. Классы Singleton<CFProtocol> и Singleton<CProtocol> - два различных класса. Попробуй так. class CProtocol { };
class CFProtocol : public CProtocol { };
typedef Singleton<CProtocol> CProtoSingleton; typedef Singleton<CFProtocol> CFProtoSingleton;
CFProtoSingleton - твой класс.
|
|
|
Записан
|
while (8==8)
|
|
|
asker
|
|
« Ответ #2 : 19-02-2007 11:32 » |
|
> Не понял, что делает класс Singleton? Контролирует количество ссылок на объект класса T?
Singleton - это паттерн проектирования, гарантирующий единственность экземляра класса в приложении. Контроль числа ссылок на синглтон дает гарантию того, что объект не будет удален, если есть ссылки на него.
> Классы Singleton<CFProtocol> и Singleton<CProtocol> - два различных класса Это не 2 разл. класса, т.к. 1) Singleton<CProtocol> - такое не возможно, т.к. CProtocol - абстактный. 2) CProtocol - это интерфейс CFProtocol.
Если я сделаю как ты сказал, то я должен положиться на человека, кот-ый сделает CF2_Protocol, чтобы он еще и сделал: "typedef Singleton<CF2Protocol> CF2_Protocol;"
К тому же, хотелось бы все таки, чтоб синглтон был как бы "заложен" в CProtocol. Т.е. чтобы он был частью интерфейса.
|
|
|
Записан
|
С уважением, asker
|
|
|
asker
|
|
« Ответ #3 : 19-02-2007 11:36 » |
|
Да и еще, почему нельзя сделать как Вы сказали: Если есть внешний класс, управляющий объектами, удовлетворяющими интерфейсу CProtocol, то он обязан (!) знать методы синглтона, чтобы уметь создавать/удалять эти объекты.
|
|
|
Записан
|
С уважением, asker
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #4 : 19-02-2007 12:57 » |
|
Как я понял, проблема заключается в том, что каждый класс иерархии с предком CProtocol нужно обернуть в одиночку. Причём делать это автоматически.
Можно попытаться воспользоваться шаблоном "фабричный метод" или "виртуальный конструктор" (Factory Method) в дополнение к одиночке.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Антон (LogRus)
|
|
« Ответ #5 : 19-02-2007 14:06 » |
|
Я бы на Вашем месте использовал фабрику или глобальные смартпоинтеры и не мучался.
Основная причина подобного в том, что зачастую(часто видел подбные реализации) синглетоны создаются на куче, что не хорошо т.к. вы не управляете временем жизни объекта
Хитрая шаблонная фабрика(или класс генератор экземпляров) позволит создавать объект необходимого типа заранее или по запросы и уничтожать по запросу или при разрушении фабрики в правильном порядке(что очень важно иногда) к тому же подобный подход позволяет обычно позволят уменьшить связанность кода между собой т.е. коду использующему лишь один объект генерируемый фабрикой не нужно знать обо всех объектах генерируемых фабрикой я когда то для этого использовал примерно следующий интерфейс class Factory { class FactoryImpl; scoped_ptr<FactoryImpl> pImpl; public: template <class T> T& Get(); }
подобный подход был выбран после БОЛЬШИХ граблей с синглетонами в подключаемой DLL
|
|
|
Записан
|
Странно всё это....
|
|
|
asker
|
|
« Ответ #6 : 19-02-2007 15:56 » |
|
LogRus, честно говоря я не понял Ваш кусок кода, а именно: 1) То, что FactoryImpl - это реализация фабрики - это понятно, но не понятен способ ее описания, т.е. что значит запись "class FactoryImpl;" 2) template <class T> T& Get(); - а что возвращает Get, т.е. Factory шаблоном не является. Как же передать "внутрь" тип T.
Что касается фабрики и фабричных методов. я собирался использовать Абстрактную фабрику, но я не понял как она может контролировать единственность или я уже запутался... иду на рсдн читать статьи про паттерны: Фабрика, и т.д.
|
|
|
Записан
|
С уважением, asker
|
|
|
Антон (LogRus)
|
|
« Ответ #7 : 19-02-2007 16:46 » |
|
class FactoryImpl; это предварительное объявление класса для использования его в умном указателе scoped_ptr из библиотеки boost Get возвращает ссылку на требуемый объект а зачем его туда передавать? есть некая общая фабрика созданная на стеке Factory gF; или на куче Factory *gpF; или со статичным методом Get обращаемся к ней для получения объекта с типом CustomClass CustomClass &cc = gF.Get<CustomClass>(); реализацию фабрики делал на основе списков типов т.к. требовался определённый порядок уничтожения созданных объектов(Александреску "Современный дизайн C++")
|
|
|
Записан
|
Странно всё это....
|
|
|
asker
|
|
« Ответ #8 : 25-02-2007 05:32 » |
|
Всем привет! Извините за задержку. LogRus вот эта строка
> CustomClass &cc = gF.Get<CustomClass>();
должна же быть написана в управляющем классе, верно, но проблема в том, что он (упр. класс) не знает типы объектов, которые будет создавать.
> Можно попытаться воспользоваться шаблоном "фабричный метод" или "виртуальный конструктор" > > (Factory Method) в дополнение к одиночке.
Т.е. запихать реализацию синглтона в интерфейс Протокола (например) + несколько фабричных методов.
> подобный подход был выбран после БОЛЬШИХ граблей с синглетонами в подключаемой DLL.
Параллельно вопрос (и наверное не один), а синглтон, реализуемый обычным образом (т.е. без использования COM) и созданный скажем в подключенной DLL будет единственным в приложении или нет.
И еще вопрос: есть приложение, в котором скажем есть что-то такое: map<int, CProtocol*> map; Сами объекты (наследники CProtocol), создаются в DLL, как передать приложению указатели на эти объекты. Варианты: 1) Использовать механизм сообщений WM_MESSAGE; 2) Это не правильный подход; 3) Предложите что-нибудь еще, пожалуйста.
Кстати, к сожалению Александреску "Современный дизайн C++" нет в магазинах (местных и в инет: books, ozon, и т.д., там целая серия была).
|
|
|
Записан
|
С уважением, asker
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #9 : 25-02-2007 14:30 » |
|
Опуская проблемы, описанные LogRus'ом, связанные с утечками памяти: #include <iostream> #include <string>
using namespace std;
// Одиночка для иерархии объектов базового класса Protocol.
// Предварительная декларация. template<class T> class Protocol;
template<class T> class Singleton { private: // Ссылка на экземпляр протокола. static Protocol<T> *_instance; protected: // Защищённые конструкторы одиночки - не позволяют создать экземпляр в обход метода // getInstance. Singleton<T>() { } Singleton<T>(const Singleton<T> ©) { } public: // Метод получения экземпляра класса-одиночки. static T *getInstance() { if(Singleton<T>::_instance == NULL) { // Класс-параметр T обязан быть потомком Protocol. Попытка // присваивания класса, не входящего // в иерархию Protocol, приведёт к несовместимости типов. Singleton<T>::_instance = new T(); } return dynamic_cast<T *>(Singleton<T>::_instance); } }; // Инициализация статического члена класса-одиночки. template<class T> Protocol<T> *Singleton<T>::_instance = NULL;
// Интерфейс протокола.
class IProtocol { public: virtual void f() = 0; };
// Базовый абстрактный класс иерархии протоколов.
template<class T> class Protocol: public Singleton<T>, public IProtocol { };
// Различные реализации протоколов.
class ProtocolA: public Protocol<ProtocolA> { public: virtual void f() { cout << "A" << endl; } };
class ProtocolB: public Protocol<ProtocolB> { public: virtual void f() { cout << "B" << endl; } };
// Основная функция.
int main() { // Тест IProtocol *p1 = ProtocolA::getInstance(); IProtocol *p2 = ProtocolB::getInstance(); IProtocol *p3 = ProtocolA::getInstance(); p1->f(); p2->f(); p3->f(); cout << string(p1 == p2 ? "p1 == p2" : "p1 != p2") << endl; cout << string(p1 == p3 ? "p1 == p3" : "p1 != p3") << endl; cout << string(p2 == p3 ? "p2 == p3" : "p2 != p3") << endl; // Логическая ошибка наследуемого одиночки. IProtocol *p4 = new ProtocolA(); p4->f(); delete p4; // getchar(); return 0; } Т.е. в принципе автоматически, без участия программиста проблема контроля экземпляров классов целой иерархии не решается. Либо конструкторы некоторого класса следует объявлять private - тогда невозможно пользователю решения создать потомков. Либо пользователь, создавая потомков решения, обязуется их конструкторы объявлять protected. Что эквивалентно вышеприведённому требованию пользователю каждый протокол объявить как Singleton<XProtocol> - о чём sss говорил. В иных случаях Singleton не работает - возможно создать экземпляры классов в обход метода getInstance. И этому моменту в книге банды четырёх должного внимания не уделено.
|
|
« Последнее редактирование: 15-12-2007 21:50 от Алексей1153++ »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Антон (LogRus)
|
|
« Ответ #10 : 25-02-2007 18:11 » |
|
CustomClass &cc = gF.Get<CustomClass>(); делает любой класс который хочет заполучить себе ссылку на объект созданный фабрикой
|
|
|
Записан
|
Странно всё это....
|
|
|
asker
|
|
« Ответ #11 : 26-02-2007 10:47 » |
|
dimka, твой вариант хороший, но есть одно НО, точнее 2. Если у меня несколько классов, которые должны быть синглтонами, значит для каждого писать свой синглтон. У меня реализация синглтона такая: template <class T> class Singleton { private: static T* self; static int refCount; protected: Singleton() {} virtual ~Singleton() { self = NULL; } public: static T* GetInstance() { if (!self) self = new T; refCount++; return self; } void FreeInst() { if(--refCount == 0) delete this; } };
dimka, еще ты специально выделил IProtocol (т.е. в этом есть какой-то особый смысл, кроме того, что это интерфейс), т.е. я это спрашиваю к тому, что я пробовал так: IProtocol нету, а все его методы в CProtocol. Теперь я CProtocol делаю template, т.е. template <class T> class CProtocol : public Singleton<T> {...}
class CConcreteProtocol : public CProtocol<CConcreteProtocol> {...}
И мне выдовало ошибку повторное описание CConcreteProtocol. Правда, у меня есть подозрение что дело было в другом, потом попробую и скажу. LogRus, я это понял. Я может быть не правильно выразился. Одна из проблем, которые стоят передо мной. Это то, что класс, который должен будет работать с объектами (тем же CProtocol) знает только их интерфейсы и больше ничего, т.е. он не знает конктретных классов. Назовем его CCore. Один из вариантов решения - это виртуальный метод, умеющий создавать экземпляры этих классов, т.е. что-то вроде Clone - если я правильно понимаю это паттерн Prototype. Ладно с этим я еще подумаю... А так LogRus, dimka, sss и все остальные большое спасибо, что откликаетесь на мои вопросы, а то я уже давно "варюсь в собственном соку" .
|
|
|
Записан
|
С уважением, asker
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #12 : 26-02-2007 12:34 » |
|
dimka, твой вариант хороший, но есть одно НО, точнее 2. Если у меня несколько классов, которые должны быть синглтонами, значит для каждого писать свой синглтон. Я в одиночку включил Protocol только для того, чтобы это был специализированный одиночка для протоколов. Естественно, если тебе нужен произвольный одиночка - используй более универсальный вариант. dimka, еще ты специально выделил IProtocol (т.е. в этом есть какой-то особый смысл, кроме того, что это интерфейс), т.е. я это спрашиваю к тому, что я пробовал так: IProtocol нету, а все его методы в CProtocol. Смысл есть. Ты в начале жаловался, что не хочешь нагружать интерфейс протокола членами одиночки и указывал это как препятствие наследования протокола от одиночки. В моём варианте такой проблемы нет. Всякую специализацию протокола можно привести к IProtocol, и методы одиночки не будут "смущать" пользователя объекта такого объекта - он их просто не увидит. И мне выдовало ошибку повторное описание CConcreteProtocol. Ошибка точно не в этом месте. Я на такое натыкался, когда пример писал. У меня была ошибка в Singleton в части объявлений "Protocol<T> *" или "T *" для универсального одиночки. Внимательно просмотри определения шаблонов. Один из вариантов решения - это виртуальный метод, умеющий создавать экземпляры этих классов, т.е. что-то вроде Clone - если я правильно понимаю это паттерн Prototype. Здесь тоже большая проблема в сочетании с одиночкой. Статические методы не бывают виртуальными. Может пойти следующим образом: создать синглетон для фабрики или прототипа, а объект фабрики или прототипа сам уже будет реализовывать синглетон для конкретного протокола. Тогда получение объекта будет типа: CProtocol *p = FactorySingleton::getInstance(/*Параметр, определяющий тип конкретной фабрики или конкретного протокола*/)->getInstance(); При этом возможно будет использовать полиморфизм различных вариантов фабрик.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Антон (LogRus)
|
|
« Ответ #13 : 27-02-2007 17:33 » |
|
Ну раз тебе нужен один указатель на интерфейс то зачем вообще "одиночки" засунь их в shared_ptr и не мучайся везде отдавай shared_ptr весь COM сплошные умные указатели на интерфейсы всё, я отказываюсь давать рекомендации не зная архитектуры системы тебе точно нужны одиночки, есть пользователи одиночки используют их только через указатели интерфейсы. в общем не вижу смысла одиночек. передал умный указатель классу пользователю(при создании или еще как) и забыл об этом, просто хранишь себе этот умный указатель и всё, впрочем хранилище можно реализовать в виде фабрики, мапы, вектора, объекта класса "список типов" или любым другим изощренным способом реализации хранилища рабочих тел от интерфейсов лучше более детально опиши архитектуру, кто создает какие объекты, как хранит передаёт, что куда? как используются интерфейсы и где? что бы перейти из обрасти теоритических изысканий в области практического применения
|
|
|
Записан
|
Странно всё это....
|
|
|
Aveic
Постоялец
Offline
Пол:
Yellow
|
|
« Ответ #14 : 04-03-2008 00:21 » |
|
ааа...., м.... ну ладно
|
|
|
Записан
|
|
|
|
|