Почему статическая переменная инициализируется дважды?

Здравствуйте! Я работаю со статической библиотекой, которая содержит точку входа. Чтобы передать данные в эту библиотеку, я создаю переменную в глобальном пространстве проекта: 
<pre><code class="cpp">MyScript* script = dynamic_cast&lt;MyScript*&gt;(new MyScript(Register::UserScriptType::Component));</code></pre>
После этого вызывается конструктор, который добавляет указатель на мой скрипт в общий контейнер: 
<pre><code class="cpp">bool Register::Registry::RegisterActorWithComponent(Register::UserScript* script, std::string objectName) {
	//Check if script cannot be converted
	if (dynamic_cast&lt;Register::UserScript*&gt;(script) == nullptr)
		return false;
	
	//Later Host will call all UserScript functions
	_userScripts.push_back(script);
	return RegisterActor(objectName);
}</code></pre>
Все эти действия происходят до вызова функции main. Однако, когда программа заходит в main, контейнер оказывается пустым, как будто его где-то заново инициализируют, и он теряет данные.

Вот класс регистратора пользовательских скриптов, который находится в статической библиотеке: 
<pre><code class="cpp">namespace Register {
	class UserScript;

	class Registry
	{
		friend class Core::Host;

	public:
		static std::vector&lt;Register::UserScript*&gt; _userScripts;

	public:
		~Registry();

		static bool RegisterActorWithComponent(Register::UserScript* script, std::string objectName = "Undefined");
		static bool RegisterActor(std::string objectName = "Undefined");
		
	private:
		static std::unique_ptr&lt;Register::Registry&gt; MakeRegistry();
		Registry();

		void RegistryStart();
		void RegistryLoop();
	};
}</code></pre>
  • 29 сентября 2024 г. 15:13
Ответы на вопрос 2
Проблема, с которой вы сталкиваетесь, вероятно, связана с тем, как статические переменные инициализируются в C++. Давайте разберем несколько ключевых моментов.

### Статические переменные

Когда вы объявляете:

```cpp
static std::vector<Register::UserScript*> _userScripts;
```

внутри класса `Registry`, это означает, что `_userScripts` будет статической переменной для этого класса. Это значит, что она существует на уровне класса, а не в пределах экземпляра. Однако, есть несколько важных аспектов инициализации статических переменных:

1. **Инициализация статических переменных**: Статические переменные класса инициализируются в порядке их объявления в коде, а не в порядке, в котором они используются. Если у вас есть несколько разных файлов, или разные модули, которые используют эту переменную, это может вызвать проблемы, если они не инициализированы в одном и том же контексте.

2. **Определение переменной**: Вы должны убедиться, что вы определяете статическую переменную в одном (и только одном) файле. Например, если вы объявили `static std::vector<Register::UserScript*> _userScripts;` в заголовочном файле, вы должны также определить ее в одном из файлов реализации:

```cpp
std::vector<Register::UserScript*> Register::Registry::_userScripts;
```

3. **Ссылки на разные пространства имен**: Если вы инициируете переменную в одном месте (например, в одном компиляционном переводе), а затем прерываете в другом (например, в другом компиляционном переводе или модуле), это может привести к созданию той же переменной с "незаполненной" памятью.

### Возможные решения

1. **Подтвердите единственность определения**: Проверьте, что `_userScripts` инициализирована только один раз, в одном из .cpp файлов, через указание:

   ```cpp
   std::vector<Register::UserScript*> Register::Registry::_userScripts;
   ```

2. **Проверьте глобальные правки**: Если вы где-либо в коде, например в другой библиотеке, используете `Register::Registry`, возможно, она также нарушает ваш контекст, если у вас множество статических библиотек с одинаковыми именами.

3. **Используйте Singleton**: Если у вас есть необходимость в едином экземпляре `Registry`, вы можете реализовать паттерн Singleton, чтобы убедиться, что у вас есть один экземпляр:

   ```cpp
   class Registry {
   public:
       static Registry& GetInstance() {
           static Registry instance; 
           return instance;
       }

   private:
       Registry() {}
       Registry(const Registry&) = delete;
       Registry& operator=(const Registry&) = delete;
   };
   ```

Таким образом, обращаясь к `Registry::GetInstance()`, вы получаете один и только один экземпляр `Registry`.

### Заключение

По всей видимости, ваша проблема вызвана тем, что вы имеете несколько определений или инициализаций статической переменной `_userScripts` в разных контекстах. Проверьте ваши файлы, чтобы убедиться, что все экземпляры и определения одного и того же класса находятся под контролем.
Первое, с чего стоит начать ответ: Порядок инициализации глобальных данных программы стандартом не определен. 
Поэтому нельзя говорить о том, какие конструкторы после каких вызываются. Сейчас это так, а завтра поменяется.
Описанный у тебя случай - это Static Initialization Order Fiasco .

Сценариев описания происходящего у тебя будет много. Детали зависят от выбранного у тебя транслятора, его версии и стандарта языка.

Как с этим справляться? Отец-основатель уже очень давно все рассказал .
Нужно просто воспользоваться стандартом языка. Тем, что стандарт предписывает инициализировать статические данные внутри функции при первом ее вызове.
Похожие вопросы