Я прочитал эффективный С++, о котором упоминается в пункте 04
Avoid initialization order problems across translation units by re-placing non-local static objects with local static objects.
Я думаю, что «глобальный и единственный объект» должен быть одноэлементным шаблоном, а не внешним объектом после того, как я прочитал этот пункт.
например, объект ввода-вывода (std::cout)
Но std::cout кажется внешним объектом. (http://www.cplusplus.com/reference/iostream/cout/)
Я смущен этим.
Я захватываю некоторый код из этой книги.
Во-первых, это плохой код:
class FileSystem {
// from your library’s header file
public:
...std::size_t numDisks( ) const;
// one of many member functions...
};
extern FileSystem tfs;
the relative order of initialization of non-local staticobjects defined in different translation units is undefined.
Таким образом, вышеупомянутый код может быть ошибкой, когда я вызываю tfs.
Потому что tfs может не завершить инициализацию.
Код рекомендации:
class FileSystem { ... }; // as before
FileSystem& tfs()
{
static FileSystem fs;
return fs;
}
class Directory { ... };// as beforeDirectory::Directory( params )
Directory::Directory( params ) // as before, except references to tfs are
//now to tfs( )
{
...
std::size_t disks = tfs().numDisks( );
...
}
Directory& tempDir()
{
static Directory td(params);
return td;
}
зачем использовать "singleton", когда можно использовать "extern"
Кстати, проблем с порядком инициализации (почти) не возникает std::cout
.
Связанные чтения: stackoverflow.com/questions/8784892/…en.cppreference.com/w/cpp/io/ios_base/Init
Использование переменной extern
обеспечивает (субъективно) более приятный синтаксис
std::cout << stuff;
Подчеркивая, что стандартный поток является уникальным объектом, а не результатом вызова какой-либо функции. Поскольку потоковая передача предназначена для выполнения в потоковых объектах, использование объектной нотации лучше с ней связано.
Что касается фиаско статического порядка инициализации, стандартная библиотека избегает его, используя для инициализации метод Счетчик Шварца. Инициализация происходит в четко определенном порядке, потому что добавление iostream
добавляет специальный глобальный объект типа Init
к включению TU. Конструктор объекта обрабатывает инициализацию потока перед его первым использованием, каким бы оно ни было.
Спасибо! Ключевое слово похоже на "Счетчик Шварца".
Потому что C++ выбрал более сложный подход, получив эффективность и простоту прямого доступа к объектам и все же избежав фиаско статического порядка инициализации.
Как?
Стандартные потоки С++ на самом деле не инициализируются статически. Вместо этого #include-ing <iostream>
определяет глобальный статический объект типа std::ios_base::Init
, который управляет глобальным счетчиком ссылок, а вместе с ним и инициализацией потоков C++.
Или, по крайней мере, так указано, но всегда есть правило «как если бы».
Потому что C++ был создан по образцу C, который имеет глобальные переменные
stdin
/stdout
/stderr
.