Pull to refresh

Comments 24

Как раз читаю Effective C++ Мейерса. RAII и умные указатели — действительно отличная вещь, во многом облегчающая жизнь.
Вместо того, чтобы полагаться или не полагаться на сгенерированные компилятором функции, можно ему явно запретить генерацию. В C++11 для этого есть "=delete":
Singleton
class Singleton {
public:
    Singleton(const Singleton& rhs) =delete;
    Singleton& operator=(const Singleton& rhs) =delete;
    static Singleton *getInstance() {
        return instance_;
    }

private:
    Singleton();
    static Singleton *instance_;
};



мутекс
Резануло глаза. В русскоязычной литературе всё-таки «мьютекс» обычно пишут.
Красивше такой вариант:
static auto instance() {
static singleton instance{};
return &instance;
}

Хотя я возвращал бы ссылку, копирование все равно запрещено.
А глобальная ссылка позволит обращаться к объекту проще.

Singleton &  singletonThe = Singleton::Instance();

Ещё требуется указать, что эта переменная одна общая для всех cpp-файлов, использующих наш Singleton.h. Например в MSVC это так:

__declspec(selectany)  Singleton &  singletonThe = Singleton::Instance();

«Нужно больше глобальных переменных».
Идея не встретила одобрения. Забавно.

Коллеги, вы предпочитаете повсюду писать Singleton::Instance() вместо singletonThe – два идентификатора вместо одного? Или какая-то другая причина?
__declspec(selectany) — это какбэ не портабельный C++. Плюс к тому, здесь не ленивая инициализация, а порядок инициализации глобальных переменных в C++ — это просто-таки поле из граблей.
Атрибут __declspec(selectany) поддерживается и в gcc, и в Borland C++, и в Clang с ключом -fms-extensions.

Не поддерживается в Intel C++. Однако его используют около 2% присутствующих (из пишущих на С++) – результаты опроса.

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

В нашем проекте так много классов с паттерном Одиночка (Singleton), что можно запутаться в порядке их инициализации. Значит это не мелкая поделка, а большой проект, а значит и автоматические тесты в нём конечно же есть.

Зная всё это, спросите себя, согласны ли вы вдвое ухудшить читабельность везде, где происходит обращение к объекту класса Singleton.
По-моему мутные глобальные пременные куда больше ухудшают читабельность программы, чем пара лишних букв в обращении к переменной.
Да. singletonInstance может быть чем угодно, какого угодно типа, уже инициализированный или еще нет. В случае с Singleton::Instance() все придельно ясно.
Это венгерская запись. Тип переменной ясен из префикса «singleton».

Про то, что можно не бояться порядка инициализации, писал выше.

Впрочем, одну деталь я упустил с самого первого примера. Глобальность переменной тоже надобно показывать префиксом. Заменяем повсюду singletonInstance и singletonThe на 
g_singletonInstance.
Когда вызываем Singleton::Instance(), мы должны опасаться, что может произойти повторный вход (reenter) в метод, из которого делаем вызов. Примерно так:

A::Instance() приводит к вызову B::Instance()
B::Instance() приводит к вызову C::Instance()

X::Instance() приводит к вызову A::Instance()

Как видим, при ленивой инициализации порядок создания объектов так же может выйти из-под контроля и создать не меньшие проблемы, чем с глобальными объектами.
Порядоком инициализации классических синглтонов хотя бы можно напрямую управлять, в отличии от порядка инициализации глобальных переменных. А круговые зависимости будут не меньшей проблемой и в случае с глобальными переменными.
По моему должно быть
static auto& instance()
При возврате по ссылке — да, либо decltype(auto). Но тут возвращается указатель.
очень люблю работать с shared_ptr на лист из shared_ptr на векторов из… ну вы поняли. У меня есть правило, если объект который я создаю будет использоваться в списках, пиши свой класс хранитель (а в внутрях если приспичит используй shared_ptr)
Правило -1: если в классе есть виртуальные методы, делайте виртуальный деструктор. И забудьте про уродливый кастомный делетер.
А про «public virtual or protected non-virtual destructor» не слышали? Не всегда нужно разрешать удаление объектов через указатель на базовый класс.
Спасибо! Было интересно. Теперь буду знать как это называется. чтоб не просто писать и объяснять почему так, а говорить «Правило ноля.» :) Подход интересный, практичный. Единственный, как мне кажется, его изъян — это при использовании его в команде, члены команды должны о нем быть осведомлены, чтоб не выкорчевывали со словами: «Это не правильно. Надо так...». И не переписывали уже сами с использованием привил 3х или 5ти, так как они более распространены.

Правильно использованное "правило ноля" не вызывает желания переписать код с использованием других правил.

Почему у вас везде «хэндлер» («обработчик») а не «хэндл» («ручка»)?
Sign up to leave a comment.

Articles