Часть 1: О компоновщике
Это известная проблема как в C, так и в C ++, и она является результатом текущей модели компиляции. Полное объяснение , как это происходит, выходит за рамки этого ответа, однако этот доклад Мэтта Годболта дает подробное объяснение процесса для начинающих. Смотрите также эту статью на компоновщике .
В 2020 году выйдет новая версия C ++, в которой будет представлена новая модель компиляции (называемая модулями), которая позволяет избежать подобных проблем. Вы сможете импортировать и экспортировать вещи из модуля, подобно тому, как пакеты работают в Java.
Часть 2. Решение вашей проблемы
Есть несколько разных решений.
Волшебное решение 1: одна уникальная глобальная переменная
Это довольно гладко. Если вы поместите глобальную переменную внутри функции как статическую переменную, она будет всегда создаваться только один раз, и это обеспечивается стандартом (даже в многопоточной среде).
#ifndef SHARED_HEADER_H_
#define SHARED_HEADER_H_
#include <iostream>
struct Data {
Data(void) {std::cout << "Constructor" << std::endl;}
~Data(void) {std::cout << "Destructor" << std::endl;}
int FuncDefinedByLib(void) const;
};
Data& getMyDataExactlyOnce() {
// The compiler will ensure
// that data only gets constructed once
static Data data;
// Because data is static, it's fine to return a reference to it
return data;
}
// Here, the global variable is a reference
extern const Data& data = getMyDataExactlyOnce();
#endif
Волшебное решение 2. Несколько различных глобальных переменных, по одной на единицу перевода
Если вы пометите глобальную переменную как встроенную в C ++ 17, то каждый модуль перевода, который включает в себя заголовок, получает свою собственную копию в своем собственном месте в памяти. См .: https://en.cppreference.com/w/cpp/language/inline
#ifndef SHARED_HEADER_H_
#define SHARED_HEADER_H_
#include <iostream>
struct Data {
Data(void) {std::cout << "Constructor" << std::endl;}
~Data(void) {std::cout << "Destructor" << std::endl;}
int FuncDefinedByLib(void) const;
};
// Everyone gets their own copy of data
inline extern const Data data;
#endif
Часть 3: Можем ли мы использовать это для создания Темной Магии?
Вид. Если вы действительно хотите использовать Dark Magic с глобальными переменными, C ++ 14 вводит templated глобальные переменные:
template<class Key, class Value>
std::unordered_map<Key, Value> myGlobalMap;
void foo() {
myGlobalMap<int, int>[10] = 20;
myGlobalMap<std::string, std::string>["Hello"] = "World";
}
Делай из того, что хочешь. У меня не было большого применения для шаблонных глобальных переменных, хотя я представляю, что если бы вы делали что-то вроде подсчета количества вызовов функции или числа созданий типа, было бы полезно сделать это.