Мой конкретный вопрос заключается в том, что при реализации singleton class в С++ существуют ли существенные различия между двумя приведенными ниже кодами относительно производительности, побочных проблем или что-то:
class singleton
{
// ...
static singleton& getInstance()
{
// allocating on heap
static singleton* pInstance = new singleton();
return *pInstance;
}
// ...
};
и это:
class singleton
{
// ...
static singleton& getInstance()
{
// using static variable
static singleton instance;
return instance;
}
// ...
};
(Обратите внимание, что разыменование в реализации на основе кучи не должно влиять на производительность, поскольку AFAIK не содержит дополнительного машинного кода, созданного для разыменования. Кажется, только синтаксис отличается от указателей.)
UPDATE:
У меня есть интересные ответы и комментарии, которые я пытаюсь обобщить здесь. (Чтение подробных ответов рекомендуется для заинтересованных.):
- В одноэлементном режиме, используя статическую локальную переменную, деструктор класса автоматически вызывается при завершении процесса, тогда как в случае динамического распределения вам нужно как-то уничтожить уничтожение объекта, например. с помощью интеллектуальных указателей:
static singleton& getInstance() {
static std::auto_ptr<singleton> instance (new singleton());
return *instance.get();
}
-
Синглтон, использующий динамическое распределение, является "более ленивым", чем статическая одноэлементная переменная, как в последнем случае, требуемая память для одноэлементного объекта (всегда?) зарезервирована при запуске процесса (как часть целая память, необходимая для загрузки программы), и только вызов одноэлементного конструктора откладывается до
getInstance()
времени вызова. Это может иметь значение, когдаsizeof(singleton)
велико. -
Оба являются потокобезопасными в С++ 11. Но с более ранними версиями С++ он специфичен для реализации.
-
В случае динамического распределения используется один уровень косвенности для доступа к одиночному объекту, тогда как в случае статического одноэлементного объекта прямой адрес объекта определяется и жестко закодирован во время компиляции.
P.S.: Я исправил терминологию, которую я использовал в оригинальной публикации в соответствии с ответом @TonyD.