Полезная шаблонная магия. Еще не singleton, но уже не global static.

Бывает, что в программе нужно обеспечить наличие единственного доступного отовсюду экземпляра какого-нибудь класса. При этом, разумеется, таскать повсюду указатель на него нам совершенно не хочется и боже упаси от глобальных переменных! Энциклопедии при этом советуют брать шаблон Singleton и воплощать его без страха и упрека в полном соответствием с курсом партии и правительства.

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

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

Выделенное слово “тип” сразу наводит на мысль о шаблонах. Шаблоны – это отличный способ генерировать столько разных типов данных, сколько нам потребуется. Например, vector<char> и vector<unsigned char> – два класса, хоть и идентичных с виду, но абсолютно разного типа. Вроде как если бы у нас были classA и classB.

Теперь вспоминаем слово static и бросаемся в бой

template <class T>
class  tStaticInstance
{
public:
        static T* GetInstance();
private:
        static T m_Instance;
}

template <class T>
T tStaticInstance<T>::m_Instance = T();

Это действительно все, что нужно, чтобы иметь глобальный статический объект класса T без необходимости явно объявлять глобальную статическую переменную в каком-нибудь заголовочном файле и потом повсюду носиться с ним. Вместо этого получаем:

#include "tStaticInstance.h"/// somewhere somewhen

tStaticInstance<MyConcreteType>::GetInstance()->SomeMethod();
tStaticInstance<OtherType>::GetInstance()->SomeMethod();

Код выглядит страшновато на первый взгляд, но на самом деле убивает сразу несколько зайцев:

  • Нет необходимости модифицировать MyConcreteType и OtherType. Главное – чтобы в них был конструктор по умолчанию
  • Можно создать статическую переменную любого типа из любого места программы без необходимости ее явного объявления
  • Нет присущих singleton’у проблем
  • Из выражения явно видно, что нам необходим единственный экземпляр этого класса. В следующих статьях я приведу пример, когда это может быть полезно.
  • Код прост как дважды два
  • Не нужно следить за глобальными статическими объектами -  если после очередной сессии рефакторинга какой-то статический объект оказался не у дел, то он больше создаваться не будет.

Естественно, такой шаблон имеет свои ограничения:

  • Уникальность экземпляра гарантируется только в пределах одного бинарника. Впрочем, организовывать синглтон, гарантирующий единственность экземпляра как в главном модуле, так и во всех dll, до сих пор берутся только сильные духом
  • Экземпляр инстанцируется всегда, даже если во время работы программы ни разу использован не будет. Тут классический singleton, безусловно, выигрывает.

Еще нет защиты от дурака, например

MyType pInstance* = tStaticInstance<MyConcreteType>::GetInstance();
delete pInstance;     // Something bad will happen

Но знаете ли, в таких вещах главное – это соблюсти баланс, иначе код получится настолько идиотозащищенным, что только идиоты и захотят им пользоваться.

Leave a comment

4 Comments.

  1. Хм, мне кажется, что уникальность-то не гарантируется как раз. То есть, все от линкера зависит, удалит он дубликаты правильно или нет.

  2. Возможно. 14.7.2/7 говорит, что

    The explicit instantiation of a class template specialization implies the instantiation of all of its members not previously explicitly specialized in the translation unit containing the explicit instantiation.

    Это гарантирует уникальность в пределах одного translation unit. На практике же архаичных линкеров уже не встречается, КМК.

  3. А что за “присущие синглтону проблемы” имеются в виду, позвольте спросить?

    И кстати, для сильных духом любителей шаблонной магии… Зачем заниматься байсиклинвеншеном, если есть Loki<library>?

  4. А что за “присущие синглтону проблемы” имеются в виду, позвольте спросить?

    Достаточно их. Один только потенциальный геморрой внутри многопоточной аппликухи при инициализации чего стоит.

    Локи – это не мое. Мы балуемся чем потяжелее, бустом там.

Leave a Reply

Yandex Mail.ru Google LiveJournal myOpenId Flickr claimId Blogger Wordpress OpenID Yahoo Technorati Vidoop Verisign AOL


[ Ctrl + Enter ]

Trackbacks and Pingbacks:

11 visitors online now
11 guests, 0 members
Max visitors today: 13 at 03:20 am MST
This month: 17 at 02-07-2012 06:17 am MST
This year: 29 at 01-23-2012 02:50 am MST
All time: 45 at 02-23-2011 09:11 am MST