Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Синглтон и наследование  (Прочитано 21289 раз)
0 Пользователей и 3 Гостей смотрят эту тему.
asker
Помогающий

ru
Offline Offline
Пол: Мужской

« : 19-02-2007 08:51 » 

Есть шаблон синглтона с контролем числа ссылок:
Код:
template <class T> class Singleton {...}

Есть класс CProtocol, который является интерфейсом:
Код:
class 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
Специалист

ru
Offline 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
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #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
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 19-02-2007 11:36 » new

Да и еще, почему нельзя сделать как Вы сказали:
   Если есть внешний класс, управляющий объектами, удовлетворяющими интерфейсу CProtocol, то
   он обязан (!) знать методы синглтона, чтобы уметь создавать/удалять эти объекты.
Записан

С уважением, asker
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 19-02-2007 12:57 » 

Как я понял, проблема заключается в том, что каждый класс иерархии с предком CProtocol нужно обернуть в одиночку. Причём делать это автоматически.

Можно попытаться воспользоваться шаблоном "фабричный метод" или "виртуальный конструктор" (Factory Method) в дополнение к одиночке.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #5 : 19-02-2007 14:06 » 

Я бы на Вашем месте использовал фабрику или глобальные смартпоинтеры и не мучался.

Основная причина подобного в том, что зачастую(часто видел подбные реализации) синглетоны создаются на куче, что не хорошо т.к. вы не управляете временем жизни объекта

Хитрая шаблонная фабрика(или класс генератор экземпляров) позволит создавать объект необходимого типа заранее или по запросы и уничтожать по запросу или при разрушении фабрики в правильном порядке(что очень важно иногда)
к тому же подобный подход позволяет обычно позволят уменьшить связанность кода между собой
т.е. коду использующему лишь один объект генерируемый фабрикой не нужно знать обо всех объектах генерируемых фабрикой
я когда то для этого использовал примерно следующий интерфейс
class Factory
{
   class FactoryImpl;
   scoped_ptr<FactoryImpl> pImpl;
public:
    template <class T> T& Get();
}

подобный подход был выбран после БОЛЬШИХ граблей с синглетонами в подключаемой DLL
Записан

Странно всё это....
asker
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 19-02-2007 15:56 » 

LogRus, честно говоря я не понял Ваш кусок кода, а именно:
1) То, что FactoryImpl - это реализация фабрики - это понятно, но не понятен способ ее описания, т.е. что значит запись "class FactoryImpl;"
2) template <class T> T& Get(); - а что возвращает Get, т.е. Factory шаблоном не является. Как же передать "внутрь" тип T.

Что касается фабрики и фабричных методов. я собирался использовать Абстрактную фабрику, но я не понял как она может контролировать единственность или я уже запутался... иду на рсдн читать статьи про паттерны: Фабрика, и т.д.
Записан

С уважением, asker
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #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
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 25-02-2007 14:30 » 

Опуская проблемы, описанные LogRus'ом, связанные с утечками памяти:

Код: (C++)
#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> &copy)
        {
        }
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)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #10 : 25-02-2007 18:11 » 

CustomClass &cc = gF.Get<CustomClass>();
делает любой класс который хочет заполучить себе ссылку на объект созданный фабрикой
Записан

Странно всё это....
asker
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 26-02-2007 12:34 » 

Цитата: asker
dimka, твой вариант хороший, но есть одно НО, точнее 2.
Если у меня несколько классов, которые должны быть синглтонами, значит для каждого писать свой синглтон.
Я в одиночку включил Protocol только для того, чтобы это был специализированный одиночка для протоколов. Естественно, если тебе нужен произвольный одиночка - используй более универсальный вариант.

Цитата: asker
dimka, еще ты специально выделил IProtocol (т.е. в этом есть какой-то особый смысл, кроме того, что это интерфейс), т.е. я это спрашиваю к тому, что я пробовал так:
IProtocol нету, а все его методы в CProtocol.
Смысл есть. Ты в начале жаловался, что не хочешь нагружать интерфейс протокола членами одиночки и указывал это как препятствие наследования протокола от одиночки. В моём варианте такой проблемы нет. Всякую специализацию протокола можно привести к IProtocol, и методы одиночки не будут "смущать" пользователя объекта такого объекта - он их просто не увидит.

Цитата: asker
И мне выдовало ошибку повторное описание CConcreteProtocol.
Ошибка точно не в этом месте. Я на такое натыкался, когда пример писал. У меня была ошибка в Singleton в части объявлений "Protocol<T> *" или "T *" для универсального одиночки. Внимательно просмотри определения шаблонов.

Цитата: asker
Один из вариантов решения - это виртуальный метод, умеющий создавать экземпляры этих классов, т.е. что-то вроде Clone - если я правильно понимаю это паттерн Prototype.
Здесь тоже большая проблема в сочетании с одиночкой. Статические методы не бывают виртуальными.

Может пойти следующим образом: создать синглетон для фабрики или прототипа, а объект фабрики или прототипа сам уже будет реализовывать синглетон для конкретного протокола. Тогда получение объекта будет типа:

Код: (C++)
CProtocol *p = FactorySingleton::getInstance(/*Параметр, определяющий тип конкретной фабрики или конкретного протокола*/)->getInstance();

При этом возможно будет использовать полиморфизм различных вариантов фабрик.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #13 : 27-02-2007 17:33 » 

Ну раз тебе нужен один указатель на интерфейс Улыбаюсь
то зачем вообще "одиночки" Улыбаюсь

засунь их в shared_ptr и не мучайся везде отдавай shared_ptr
весь COM сплошные умные указатели на интерфейсы

всё, я отказываюсь давать рекомендации не зная архитектуры системы Улыбаюсь

тебе точно нужны одиночки, есть пользователи одиночки используют их только через указатели интерфейсы. Улыбаюсь в общем не вижу смысла одиночек. передал умный указатель классу пользователю(при создании или еще как) и забыл об этом, просто хранишь себе этот умный указатель и всё, впрочем хранилище можно реализовать в виде фабрики, мапы, вектора, объекта класса "список типов" или любым другим изощренным способом Улыбаюсь реализации хранилища рабочих тел от интерфейсов

лучше более детально опиши архитектуру, кто создает какие объекты, как хранит передаёт, что куда? как используются интерфейсы и где?
что бы перейти из обрасти теоритических изысканий в области практического применения
Ага
Записан

Странно всё это....
Aveic
Постоялец

ru
Offline Offline
Пол: Мужской
Yellow


« Ответ #14 : 04-03-2008 00:21 » 

ааа...., м.... ну ладно Улыбаюсь
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines