Ответ 1
-
версия
new
, очевидно, должна выделять память во время выполнения, тогда как версия без указателя имеет память, выделенную во время компиляции (но обе они должны выполнять одну и ту же конструкцию) -
версия
new
не будет вызывать деструктор объекта при завершении программы, но версия неnew
: вы можете использовать интеллектуальный указатель для исправления этого- вам нужно быть осторожным, чтобы некоторые деструкторы объекта static/namespace-scope не вызывали ваш синглтон после того, как его статический локальный деструктор экземпляра запустился... если вас это беспокоит, вы, возможно, читаете немного больше о Синглтон жизни и подходы к управлению ими. Andrei Alexandrescu Современный дизайн С++ имеет очень читаемое обращение.
-
в С++ 03, он определяется реализацией, будет ли поток безопасен. (Я считаю, что GCC имеет тенденцию быть, в то время как Visual Studio не стремится к тому, чтобы подтвердить/исправить оценку.)
-
в С++ 11, это безопасно: 6.7.4 "Если элемент управления входит в объявление одновременно при инициализации переменной, одновременное выполнение должно ждать завершения инициализации". (без рекурсии).
Обсуждение re compile-time по сравнению с распределением и инициализацией во время выполнения
Из того, как вы сформулировали свое резюме и несколько комментариев, я подозреваю, что вы не полностью понимаете тонкий аспект распределения и инициализации статических переменных....
Скажите, что ваша программа имеет 3 локальных статических 32-разрядных int
- a
, b
и c
- в разных функциях: компилятор может скомпилировать двоичный файл, который сообщает загрузчику ОС оставить 3x32-бит = 12 байт памяти для этой статики. Компилятор решает, что делает смещение каждой из этих переменных: он может помещать a
в смещение 1000 hex в сегменте данных, b
на 1004 и c
на 1008. Когда программа выполняется, загрузчик ОС не имеет значения, t нужно выделять память для каждого отдельно - все, что он знает, это общее количество из 12 байтов, которое может быть запрошено или не спрошено специально для 0-инициализации, но может потребоваться сделать так или иначе, чтобы гарантировать, что процесс не может видеть оставил содержимое памяти из других программ пользователей. Команды машинного кода в программе, как правило, жестко кодируют смещения 1000, 1004, 1008 для доступа к a
, b
и c
- поэтому при выполнении во время выполнения не требуется выделение этих адресов.
Динамическое распределение памяти отличается тем, что указатели (например, p_a
, p_b
, p_c
) будут указаны адреса во время компиляции, как описано, но дополнительно:
- указательная память (каждая из
a
,b
иc
) должна быть найдена во время выполнения (обычно, когда статическая функция выполняется первым, но компилятор разрешает делать это раньше, комментарий к другому ответу), и- если в процессе работы операционной системой слишком мало памяти для динамического выделения для успеха, тогда библиотека программ запросит ОС для большей памяти (например, с помощью
sbreak()
)), которые ОС обычно будет уничтожать по соображениям безопасности - динамические адреса, назначенные для каждого из
a
,b
иc
, должны быть скопированы обратно в указателиp_a
,p_b
иp_c
.
- если в процессе работы операционной системой слишком мало памяти для динамического выделения для успеха, тогда библиотека программ запросит ОС для большей памяти (например, с помощью
Этот динамический подход явно более запутан.