![]() |
Здравствуйте, гость ( Вход | Регистрация )
![]() ![]() |
![]() |
Litkevich Yuriy |
![]() ![]()
Сообщение
#1
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
Подбираю шаблон проектирования для моей задачи.
Задача такая: Есть программа типа MDI, хочу сделать в ней многопользовательскую работу, в одно время работает только один пользователь. В зависимости от группы пользователя ему становится доступным определенный, для данной группы, набор пунктов меню. Я это так прикинул: 1-Нужно хранить переменную, например, uid, в которой указан код текущего пользователя/группы. 2-Нужен диалог ввода имени и пароля, который будет где-нибудь искать есть ли такая пара логин/пароль. Если есть, то устанавливать найденное значение в переменной uid. Если нет - ругнутся на пользователя. 3-Основное окно программы получает от диалога результат: ОК-пользователь найден, надо обновить меню, НЕ ОК - пользователь не найден, ничего не делать. Но я подумал, что какому нибудь еще окну может понадобится знать uid, и поэтому я думаю, что простая глобальная переменная будет неудобна, и хочу сделать класс, в котором будет реализовандиалог авторизации и прочие штуки в том числе статическая переменная uid, а в других классах динамически создавать экземпляры, если сделать этот класс как "одиночку", то uid должен быть общим для всех экземпляров. ---- Вот такие мысли в моей голове, может уже есть для подобной задачи отработаный подход, и моя мысль слишком замудренная? Просвятите пожалуйста. |
|
|
ViGOur |
![]()
Сообщение
#2
|
![]() Мастер ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модератор Сообщений: 3296 Регистрация: 9.10.2007 Из: Москва Пользователь №: 4 Спасибо сказали: 231 раз(а) Репутация: ![]() ![]() ![]() |
Для твоей задачи можно использовать как глобальную переменную, так и Singleton. Singleton правда "красивше".
![]() Немогу понять, что в чем именно заключается вопрос? Если в реализации Singleton'а, то это достаточно просто: Примерно так, на наличие ошибок завтра проверю, а то сейчас уже голова не варит и студии под рукой нет. ![]() Сообщение отредактировал ViGOur - 5.5.2008, 9:12 |
|
|
Litkevich Yuriy |
![]()
Сообщение
#3
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
|
|
|
Litkevich Yuriy |
![]()
Сообщение
#4
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
слушай а функции InitInstance() и FreeInstance(); разве не должны быть объявлены как static, ведь они имеют дело со статической переменной?
|
|
|
ViGOur |
![]()
Сообщение
#5
|
![]() Мастер ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модератор Сообщений: 3296 Регистрация: 9.10.2007 Из: Москва Пользователь №: 4 Спасибо сказали: 231 раз(а) Репутация: ![]() ![]() ![]() |
Угу, именно так, только не потому, что статические переменные члены использует, а чтобы не создавать объект, который запрещено создавать вне класса.
![]() Поправил. |
|
|
Litkevich Yuriy |
![]()
Сообщение
#6
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
еще вопрос по теме задачи:
В голове крутится вариант без "Одиночки", т.е. класс прсто содержащий переменную uid обявленную как static, т.е. у всех классов она общая, краем уха слышал, что такой вариант чреват последствиями, но какими не знаю, может прояснишь? |
|
|
ViGOur |
![]()
Сообщение
#7
|
![]() Мастер ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модератор Сообщений: 3296 Регистрация: 9.10.2007 Из: Москва Пользователь №: 4 Спасибо сказали: 231 раз(а) Репутация: ![]() ![]() ![]() |
Да никаких последствий вроде не должно быть.
![]() |
|
|
Litkevich Yuriy |
![]()
Сообщение
#8
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
положим "одиночку" я кладу в два файла singleton.h и singleton.cpp, заголовочный подстегиваю к разным файлам, которым он может понадобиться, а в каком месте программы объявлять статические переменные:
![]() |
|
|
ViGOur |
![]()
Сообщение
#9
|
![]() Мастер ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модератор Сообщений: 3296 Регистрация: 9.10.2007 Из: Москва Пользователь №: 4 Спасибо сказали: 231 раз(а) Репутация: ![]() ![]() ![]() |
Принципиальной разницы не вижу, я обычно это делаю в *.cpp.
![]() |
|
|
Litkevich Yuriy |
![]()
Сообщение
#10
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
просто я подумал, что если в хидере будет, то он несколько раз будет скормлен компилеру, и в последствии линкер может подавиться, или не подавится?
|
|
|
ViGOur |
![]()
Сообщение
#11
|
![]() Мастер ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модератор Сообщений: 3296 Регистрация: 9.10.2007 Из: Москва Пользователь №: 4 Спасибо сказали: 231 раз(а) Репутация: ![]() ![]() ![]() |
Для этого и существуют такие директивы препороцессора как:
![]() |
|
|
Litkevich Yuriy |
![]()
Сообщение
#12
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
хе, правильно хидер то один и тот же, туплю
![]() |
|
|
Tonal |
![]()
Сообщение
#13
|
![]() Активный участник ![]() ![]() ![]() Группа: Участник Сообщений: 452 Регистрация: 6.12.2007 Из: Новосибирск Пользователь №: 34 Спасибо сказали: 69 раз(а) Репутация: ![]() ![]() ![]() |
Обычно синглетон (одиночка) без FreeInstance и без подсчёта ссылок пишется.
Простейший:
Мне кажется, он хорошо подойдёт, т.к. те проблемы, которые он имеет, в данном случае не существенны. Собственно проблемы: 1) Возможные гонки потоков при создании. Решаются или более хитрым методом instance - с правильными блокировками, или принудительным созданием до запуска потоков. 2) Обращение к разрушенному объекту. Когда кто-нибудь пытается обратится к instance, когда inst уже разрушен. Т.к. разрушается экземпляр такого одиночки после выхода из main, то такое может происходить, только если обращение идёт из деструкторов других глобальных объектов. Решение - или запретить такие обращения, или создавать одиночку в куче и никогда не убивать (хорошо описано у Александреску). 3) Неясный порядок разрушения. Когда такой одиночка захватывает ресурсы время жизни которых завязано на другие ресурсы, неподконтрольные одиночке - например он держит открытый запрос, а конектом к базе занимается приложение. Решение - не делать так! ![]() Другое решение - предоставить метод очистки, который должен вызываться перед закрытием коннекта к базе. После закрытия, или возвращать признак ошибки, либо брасать исключение (может быть опасно если вылетит в деструкторе), либо возвращать заглушки. ![]() Сообщение отредактировал Tonal - 5.5.2008, 11:04 |
|
|
Litkevich Yuriy |
![]()
Сообщение
#14
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
|
|
|
Andrew Selivanov |
![]()
Сообщение
#15
|
![]() Участник ![]() ![]() Группа: Участник Сообщений: 249 Регистрация: 9.10.2007 Из: Москва Пользователь №: 3 Спасибо сказали: 15 раз(а) Репутация: ![]() ![]() ![]() |
хе, правильно хидер то один и тот же, туплю ![]() Посмотри книгу Modern C++ Design: Generic Programming and Design Patterns Applied By Andrei Alexandrescu. Там все очень хорошо про singleton-ы и прочие шаблоны расписано. О, оказывается она у меня есть на русском ![]() |
|
|
Litkevich Yuriy |
![]()
Сообщение
#16
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
у меня есть Эрих Гамма, там тоже есть примеры, но пока для меня это ново и требует время на переваривание, тем более, что я незнаю как лучше применить этот шаблон, толи сделать его универсальным, в отдельном модуле в виде шаблонного класса, и потом делать от него наследование, либо сделать класс только для моей задачи, но если мне опять понадобится "одиночка" писать заново.
|
|
|
ViGOur |
![]()
Сообщение
#17
|
![]() Мастер ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модератор Сообщений: 3296 Регистрация: 9.10.2007 Из: Москва Пользователь №: 4 Спасибо сказали: 231 раз(а) Репутация: ![]() ![]() ![]() |
|
|
|
Litkevich Yuriy |
![]()
Сообщение
#18
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
да, про конструктор/деструктор я понял, это чтоб никто не мог создать дубликат
|
|
|
Litkevich Yuriy |
![]()
Сообщение
#19
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
Например, в Qt'ях я применяю такую строку:
Если я ни где не создаю экземпляр класса QTextCodec, то можно ли пользоватся таким вариантом в место "одиночки"? Правильно ли я понял, что "одиночка" спасает от "случайного" создания/удаления экземпляра? |
|
|
ViGOur |
![]()
Сообщение
#20
|
![]() Мастер ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модератор Сообщений: 3296 Регистрация: 9.10.2007 Из: Москва Пользователь №: 4 Спасибо сказали: 231 раз(а) Репутация: ![]() ![]() ![]() |
Тот пример, что ты привел, обычная статическая функция.
Класс одиночка - это класс для которого можно создать только один объект... |
|
|
Litkevich Yuriy |
![]()
Сообщение
#21
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
=1=
ViGOur, у меня компилер ругается на такую строчку, из твоего варианта "одиночки":
говорит так: Цитата singleton.h:26: error: ISO C++ forbids declaration of `operator=' with no type я его пока закоментировал и вроде проблем не испытываю, но хочется чтоб все было правильно, что нужно исправить? =2= Обязательно ли мои методы, не отоносящиеся к основным "одиночки" типа InitInstance() и FreeInstance(), помечать как sttic? |
|
|
Andrew Selivanov |
![]()
Сообщение
#22
|
![]() Участник ![]() ![]() Группа: Участник Сообщений: 249 Регистрация: 9.10.2007 Из: Москва Пользователь №: 3 Спасибо сказали: 15 раз(а) Репутация: ![]() ![]() ![]() |
=1= ViGOur, у меня компилер ругается на такую строчку, из твоего варианта "одиночки":
говорит так: Цитата singleton.h:26: error: ISO C++ forbids declaration of `operator=' with no type я его пока закоментировал и вроде проблем не испытываю, но хочется чтоб все было правильно, что нужно исправить? Попробуй вот так специфицировать
|
|
|
ViGOur |
![]()
Сообщение
#23
|
![]() Мастер ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модератор Сообщений: 3296 Регистрация: 9.10.2007 Из: Москва Пользователь №: 4 Спасибо сказали: 231 раз(а) Репутация: ![]() ![]() ![]() |
|
|
|
Litkevich Yuriy |
![]()
Сообщение
#24
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
тогда вот так ругается :
Цитата singleton.h:26: warning: no return statement in function returning non-void хоть и не ошибка, но неприятно. |
|
|
LuckLess |
![]()
Сообщение
#25
|
Студент ![]() Группа: Новичок Сообщений: 31 Регистрация: 10.10.2007 Пользователь №: 8 Спасибо сказали: 0 раз(а) Репутация: ![]() ![]() ![]() |
return *this;
|
|
|
Litkevich Yuriy |
![]()
Сообщение
#26
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
LuckLess, да вот так:
вообще не ругается |
|
|
LuckLess |
![]()
Сообщение
#27
|
Студент ![]() Группа: Новичок Сообщений: 31 Регистрация: 10.10.2007 Пользователь №: 8 Спасибо сказали: 0 раз(а) Репутация: ![]() ![]() ![]() |
еще такой вариант синглтона..
Причина редактирования: поправил тег: code=cpp, так нагляднее
|
|
|
Litkevich Yuriy |
![]()
Сообщение
#28
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
а для чего int I, она ведь неиспользуется, в шаблоне?
|
|
|
LuckLess |
![]()
Сообщение
#29
|
Студент ![]() Группа: Новичок Сообщений: 31 Регистрация: 10.10.2007 Пользователь №: 8 Спасибо сказали: 0 раз(а) Репутация: ![]() ![]() ![]() |
для того чтобы ты мог создать два разных синглтона, одного типа.
например у тебя есть class User; ты хочеш чтобы был скажем глобальный залогиненный юзер - пишеш typedef LLSingleton<User> LoggedUser; далее ты хочеш еще иметь некого глобального юзера, под которым программа имеет доступ к базе.. пишеш typedef LLSingleton<User, 1> DbUser; получиш два разных синглтона, которые оба возвращают User*. |
|
|
Litkevich Yuriy |
![]()
Сообщение
#30
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
с каждым днем начинаю понимать пользу от единственного экземпляра класса, раньше думал: "пиши окуратно и дело с концом", а на практике выходит, что окуратность особо и не причем
![]() |
|
|
Litkevich Yuriy |
![]()
Сообщение
#31
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
В итоге получилось так:
singleton.h
singleton.cpp
|
|
|
Влад |
![]()
Сообщение
#32
|
Участник ![]() ![]() Группа: Участник Сообщений: 146 Регистрация: 20.3.2009 Из: Санкт-Петербург Пользователь №: 627 Спасибо сказали: 46 раз(а) Репутация: ![]() ![]() ![]() |
Юрий, по твоей реализации (кстати, практически классической!) у меня только два замечания:
1. конструкторы-деструкторы, которые ты хочешь скрыть, не обязательно реализовывать с пустым телом {}, достаточно просто объявить их private; 2. учти, что эта реализация работает только в строго однопоточной модели! В многопоточной ты либо "влетишь", либо надо реализовывать Double-Check Locking. Это, впрочем, тоже классика. А так ничего, вполне себе красиво!... |
|
|
Litkevich Yuriy |
![]()
Сообщение
#33
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
1. конструкторы-деструкторы, которые ты хочешь скрыть, не обязательно реализовывать с пустым телом {}, достаточно просто объявить их private; они у меня с пустым телом, т.к. в моём применении они были не нужны.надо реализовывать Double-Check Locking. имеется в виду для основных переменных "Одиночки" (*p_this и cnt)?
|
|
|
AD |
![]()
Сообщение
#34
|
Профессионал ![]() ![]() ![]() ![]() ![]() Группа: Участник Сообщений: 2003 Регистрация: 4.2.2008 Из: S-Petersburg Пользователь №: 84 Спасибо сказали: 70 раз(а) Репутация: ![]() ![]() ![]() |
1. конструкторы-деструкторы, которые ты хочешь скрыть, не обязательно реализовывать с пустым телом {}, достаточно просто объявить их private; они у меня с пустым телом, т.к. в моём применении они были не нужны.Ну вот, раз не нужны, то можно объявлять вообще без тела! имеется в виду для основных переменных "Одиночки" (*p_this и cnt)? Нет. В смысле сделать так:
|
|
|
Влад |
![]()
Сообщение
#35
|
Участник ![]() ![]() Группа: Участник Сообщений: 146 Регистрация: 20.3.2009 Из: Санкт-Петербург Пользователь №: 627 Спасибо сказали: 46 раз(а) Репутация: ![]() ![]() ![]() |
Не совсем так. Правильный (имхо) пример кода приведен ниже:
|
|
|
Litkevich Yuriy |
![]()
Сообщение
#36
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
Влад, поясни пожалуйста свой код.
И m_cs что такое? |
|
|
AD |
![]()
Сообщение
#37
|
Профессионал ![]() ![]() ![]() ![]() ![]() Группа: Участник Сообщений: 2003 Регистрация: 4.2.2008 Из: S-Petersburg Пользователь №: 84 Спасибо сказали: 70 раз(а) Репутация: ![]() ![]() ![]() |
|
|
|
Litkevich Yuriy |
![]()
Сообщение
#38
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
AD, указатель в его примере - m_me
|
|
|
Влад |
![]()
Сообщение
#39
|
Участник ![]() ![]() Группа: Участник Сообщений: 146 Регистрация: 20.3.2009 Из: Санкт-Петербург Пользователь №: 627 Спасибо сказали: 46 раз(а) Репутация: ![]() ![]() ![]() |
Ага, этот пример кода выдран с корнем из реально работающего (уж года два как... в режиме 24x7) проекта. m_me - это как раз и есть объект-одиночка, а m_cs - это объект блокировки (под MFC это критическая секция, но, в принципе, с таким же точно успехом при минимальной модификации кода может быть и posix-ный mutex). В коде принципиально, что m_me должен быть именно volatile.
Это - просто пример реализации идиомы Double-Checked Locking, а более подробно о ней может рассказать и гугл; по-моему, тема подробно освещена у классиков типа Мейерса, Саттера.... вот только не помню, у кого точно. Сообщение отредактировал Влад - 28.9.2009, 20:49 |
|
|
Litkevich Yuriy |
![]()
Сообщение
#40
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
|
|
|
AD |
![]()
Сообщение
#41
|
Профессионал ![]() ![]() ![]() ![]() ![]() Группа: Участник Сообщений: 2003 Регистрация: 4.2.2008 Из: S-Petersburg Пользователь №: 84 Спасибо сказали: 70 раз(а) Репутация: ![]() ![]() ![]() |
|
|
|
Litkevich Yuriy |
![]()
Сообщение
#42
|
![]() разработчик РЭА ![]() ![]() ![]() ![]() ![]() ![]() ![]() Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: ![]() ![]() ![]() |
ага, уже разобрался, в Вкикпедии прочитал
|
|
|
Влад |
![]()
Сообщение
#43
|
Участник ![]() ![]() Группа: Участник Сообщений: 146 Регистрация: 20.3.2009 Из: Санкт-Петербург Пользователь №: 627 Спасибо сказали: 46 раз(а) Репутация: ![]() ![]() ![]() |
Гм, ну если уж говорить о кроссплатформе, то наиболее изящным решением представляется применение boost::call_once вкупе с boost::once_flag.
|
|
|
![]() ![]() ![]() |
![]() |
|
Текстовая версия | Сейчас: 10.4.2025, 6:59 |