Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Странная проблема с реализацией синглтон-класса 
:(
    Опции темы
Alpher
Дата 5.2.2007, 17:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 5.2.2007

Репутация: нет
Всего: нет



Доброго времени суток, уважаемые!
Столкнулся со следующей проблемой и туплю уже пару дней:
(те вещи, которые к делу не относятся, для краткости я опустил)

Файл Singleton.h
Код

template<typename T> class Singleton
{
public:
    Singleton() { assert(!_glob); _glob = static_cast<T*>(this); }
    virtual ~Singleton() { assert(_glob); _glob = 0; }
    static T &Inst() { assert(_glob); return *_glob; }
    static T *Ref() { return _glob; }
protected:
    static T *_glob;
};


Файл LibManager.h
Код

#include "Singleton.h"

// _UT_EXPORT определен, как __declspec(dllexport)

class _UT_EXPORT LibManager : public Singleton<LibManager>
{
public:
    LibManager(String &root);
    int EnumLibraries(UT_LIB_TYPE type, StringList &libs);
    ExtLib *CreateLib(String path);

    static LibManager &Inst();
    static LibManager *Ref();
protected:
    String _root;
};


Файл LibManager.cpp
Код

#include "LibManager.h"

template<> LibManager *Singleton<LibManager>::_glob = 0;
......


Компилятор (MSVC8) выдает следующее:
warning C4661: 'LibManager *Singleton<T>::_glob' : no suitable definition provided for explicit template instantiation request

Ну и линкер, соответственно:
error LNK2001: unresolved external symbol "protected: static class PlatformManager * PlatformManager::_glob" .....

Никак не могу понять, где косяк-то? Определение для _glob ведь присутствует...
PM MAIL   Вверх
archimed7592
Дата 5.2.2007, 17:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



Цитата(Alpher @  5.2.2007,  17:01 Найти цитируемый пост)
template<> LibManager *Singleton<LibManager>::_glob = 0;
не пойму че здесь написано smile type *type::field = 0; это как?
почему бы не сделать что-то типа
Код
class S
{
public:
S *getInst ()
{
static S s;
return &s;
};
private:
S ();
~S ();
S (const S &);
operator = (const S &);
}



--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
Alpher
Дата 5.2.2007, 17:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 5.2.2007

Репутация: нет
Всего: нет



Цитата(archimed7592 @  5.2.2007,  17:11 Найти цитируемый пост)
template<> LibManager *Singleton<LibManager>::_glob = 0;
не пойму че здесь написано smile type *type::field = 0; это как?


Тут написано, что статическое поле _glob, унаследованное классом LibManager от шаблонного класса Singleton, являющееся указателем на LibManager равно нулю  smile 

Только вот почему компилятор этого не замечает?  smile 

P.S. С синтаксической точки зрения тут все ок

Насчет
Код

class S
{
public:
S *getInst ()
{
static S s;
return &s;
};
private:
S ();
~S ();
S (const S &);
operator = (const S &);
}


Это, конечно, вариант, но мне нужно будет в будущем:
1) подсчитывать ссылки и иметь возможность все-таки уничтожить экземпляр
2) наследовать от этого класса кучу других

Это сообщение отредактировал(а) Alpher - 5.2.2007, 17:22
PM MAIL   Вверх
Любитель
Дата 5.2.2007, 17:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Программист-романтик
****


Профиль
Группа: Комодератор
Сообщений: 3645
Регистрация: 21.5.2005
Где: Воронеж

Репутация: 24
Всего: 92



Alpher, у тебя код честно говоря напоминает принцип - чтоб враг не понял.
По делу - почему в принципе инитить в ноль должен наследник???
Логичней вместо указателя иметь в предке протектед статик метод:
Код

static T*& getGlob()
{
    static T* glob = 0;
    retrun glob;
}


Инициализация будет выполнена при первом вызове метода (правило инициализация локальных статик-переменных).

Добавлено @ 17:47 
А то что ты хотел пишется так по идее:
Код

LibManager* LibManager::_glob = 0;

Заметь - разные вещи.


--------------------
PM MAIL ICQ Skype   Вверх
Alpher
Дата 5.2.2007, 18:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 5.2.2007

Репутация: нет
Всего: нет



Цитата(Любитель @  5.2.2007,  17:44 Найти цитируемый пост)
Alpher, у тебя код честно говоря напоминает принцип - чтоб враг не понял.
По делу - почему в принципе инитить в ноль должен наследник???
Логичней вместо указателя иметь в предке протектед статик метод:


Да, логичней. В случае, если наследник один.
Если их много и статический указатель на экземпляр будет реализован в предке, то (как я понимаю, т.е. IMHO smile ) они будут использовать этот единственный указатель. А мне надо, чтобы у каждого наследника он был свой, соотв. его реализация и вынесена в наследника.
PM MAIL   Вверх
Любитель
Дата 5.2.2007, 18:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Программист-романтик
****


Профиль
Группа: Комодератор
Сообщений: 3645
Регистрация: 21.5.2005
Где: Воронеж

Репутация: 24
Всего: 92



 smile Ничего подобного.

Пару примеров:
Код

class A
{ static int field; }

class B: A
{}


Где бы ты не инитил field он будет один и тот же!!!

Код

template <typename T>
class A
{ static int field; }

class B: A<B>
{}

class C: A<C>
{}


A<B> и A<C> - два разных типа. У них будут два разных field. A - это вовсе не тип, это шаблон типа. У A<B> и B будет один field, потомучто B наследует от A<B>, но у A<C> и C - другой.


--------------------
PM MAIL ICQ Skype   Вверх
Alpher
Дата 5.2.2007, 18:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 5.2.2007

Репутация: нет
Всего: нет



Цитата(Любитель @  5.2.2007,  18:39 Найти цитируемый пост)
A<B> и A<C> - два разных типа. У них будут два разных field. A - это вовсе не тип, это шаблон типа. У A<B> и B будет один field, потомучто B наследует от A<B>, но у A<C> и C - другой.


Виноват, совсем забыл, что родитель - шаблонный  smile 
В общем, это - выход  smile 

Но все же, почему же приведенная мной конструкция не собирается? (Уже из чисто спортивного интереса smile )
В рамках одного модуля - все ок, а когда подключаю к приложению - вопит линкер.
Не понятно, почему он вопит, т.к. в самом приложении _glob не используется и приложению должно быть фиолетово, экспортируется оно или нет...
PM MAIL   Вверх
Любитель
Дата 5.2.2007, 18:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Программист-романтик
****


Профиль
Группа: Комодератор
Сообщений: 3645
Регистрация: 21.5.2005
Где: Воронеж

Репутация: 24
Всего: 92



Я выше писал - читай внимательно. Заметь, что LibraryManager и Singleton<LibraryManager> - два разных класса. И первый у тебя нигде явно не инстансируются, посему инит его полей (хотя стати поля у них и одни и те же) невозможен.


--------------------
PM MAIL ICQ Skype   Вверх
Alpher
Дата 5.2.2007, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 5.2.2007

Репутация: нет
Всего: нет



Цитата(Любитель @  5.2.2007,  18:54 Найти цитируемый пост)
Я выше писал - читай внимательно. Заметь, что LibraryManager и Singleton<LibraryManager> - два разных класса. И первый у тебя нигде явно не инстансируются, посему инит его полей (хотя стати поля у них и одни и те же) невозможен. 


Сейчас попробовал:

LibraryManager *LibraryManager::_glob = 0;

и

template<> LibraryManager *Singleton<LibraryManager>::_glob = 0;

дают абсолютно одинаковые результаты (в смысле, тот же самый ворнинг при компиляции и те же ошибки при линковке)
PM MAIL   Вверх
Любитель
Дата 5.2.2007, 19:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Программист-романтик
****


Профиль
Группа: Комодератор
Сообщений: 3645
Регистрация: 21.5.2005
Где: Воронеж

Репутация: 24
Всего: 92



Не знаю тогда - без компилера не берусь гадать.


--------------------
PM MAIL ICQ Skype   Вверх
Alpher
Дата 5.2.2007, 19:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 5.2.2007

Репутация: нет
Всего: нет



Цитата(Любитель @  5.2.2007,  19:09 Найти цитируемый пост)
Не знаю тогда - без компилера не берусь гадать. 


Ладно, на досуге поковыряю. Если разберусь - закину причины и решение.

P.S. За совет - спасибо  smile 
PM MAIL   Вверх
Vyacheslav
Дата 5.2.2007, 19:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 9
Всего: 59



А вообще весьма любопытная концепция синглтона smile 
Обычно смысл его в том, чтобы гарантировно обеспечить создание единственного экземпляра: второй просто создать невозможно. А у Вас при создании второго, а содать его можно, гарантированый вылет программы через assert при дебажной сборке и просто вылет при релизной.



--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Alpher
Дата 5.2.2007, 19:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 5.2.2007

Репутация: нет
Всего: нет



Цитата(Vyacheslav @  5.2.2007,  19:23 Найти цитируемый пост)
А вообще весьма любопытная концепция синглтона smile 
Обычно смысл его в том, чтобы гарантировно обеспечить создание единственного экземпляра: второй просто создать невозможно. А у Вас при создании второго, а содать его можно, гарантированый вылет программы через assert при дебажной сборке и просто вылет при релизной.


Конструктор/деструктор наследника пока висят в public для дебажных целей. Когда допишу класс, который будет собирать все синглтоны для использования приложением (и будет френдом для этих синглтонов), перенесу в protected  smile 

Смысл моих мытарств заключается в том, чтобы централизованно создать экземпляры различных синглтонов, а потом их использовать из любого места программы через класс-ядро. Т.е. что-то вроде:

Core::Inst().GetLibManager().doSomething();

Это сообщение отредактировал(а) Alpher - 5.2.2007, 19:45
PM MAIL   Вверх
Vyacheslav
Дата 5.2.2007, 19:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 9
Всего: 59



И что это даст? smile  Все равно что-то Вы наружу для содания экземляра класса оставите. И тогда кто мешает запустить эту процедуру раньше? Это, во-первых. Во-вторых, синглтон как правило гарантирует, что он будет создан до любого первого обращения к нему.
Цитата(Alpher @  5.2.2007,  19:38 Найти цитируемый пост)
Смысл моих мытарств заключается в том, чтобы централизованно создать экземпляры различных синглтонов, а потом их использовать из любого места программы через класс-ядро. Т.е. что-то вроде:

То есть при разработке  Вашей программы нужно всегда помнить о том, что прежде чем обратиться к чему либо, нужно проделать процедуру инициализации?
Но тогда зачем это называть красивыми словом Singleton. Тогда проще класс с набором статических полей, проинициализировать их с помощью того же статического метода и обращаться  к ним через теже статические методы.  








--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Alpher
Дата 5.2.2007, 21:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 5.2.2007

Репутация: нет
Всего: нет



Цитата(Vyacheslav @  5.2.2007,  19:53 Найти цитируемый пост)
То есть при разработке  Вашей программы нужно всегда помнить о том, что прежде чем обратиться к чему либо, нужно проделать процедуру инициализации?
Но тогда зачем это называть красивыми словом Singleton. Тогда проще класс с набором статических полей, проинициализировать их с помощью того же статического метода и обращаться  к ним через теже статические методы.  


Не совсем так.

Красивое слово Singleton было выбрано, т.к. оно короче, чем PseudoSingleton smile
Инициализация экземпляров происходит единовременно при инициализации класса-ядра. Пользователь может обращаться к ним только через Core::Inst().getЧтоНибудь().doЧтоНибудь() или ЧтоНибудьКласс::Inst().doЧтоНибудь()  smile 

Насчет статических полей и методов:
В случае одного уровня наследования это действительно было бы проще и удобней.
В моем случае требуется, грубо говоря, обеспечить возможность наследовать от производных (по отношению к Singleton) классов еще какие-либо, причем совершенно произвольно. Т.е., скажем:

class LibManager : public Singleton<LibManager>
class PluginManager : public LibManager, public PluginFunctions
class RichPluginManager : public PluginManager, public CoolFunctions
и т.п.

Сомневаюсь, что в случае со статическими полями и методами в LibManager это было бы удобно.
Городить темплейты на все и вся, как мне кажется, здесь нецелесообразно.
Потом, в Т.З. указана возможность наращивания функционала, т.е. появления каких-либо не предусмотренных сейчас классов.

В моем случае нужно перегрузить только Inst() и Ref(), а также определить _glob.

P.S. Согласен, без определения _glob в потомках можно обойтись  smile

Добавлено @ 21:36 
Кстати, нашел причину первоначальной проблемы  smile 

Банально, в хедере, где определяется _UT_EXPORT была опечатка и оно компилилось как __declspec(dllimport)  smile 
Сейчас конструкция, приведенная в первом посте отлично работает.

Это сообщение отредактировал(а) Alpher - 5.2.2007, 21:29
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0590 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.