Я разрабатываю библиотеку объектов и функций и имею файл заголовка, который называется super.hpp
, который содержит некоторые задачи инициализации.Повторная инициализация структуры в заголовке
super.hpp
#ifndef H_INIT
#define H_INIT
#include <iostream>
#include <string>
static bool isInit = false;
struct settings_struct{
std::string path = "foo";
void load(){ path = "bar"; }
};
struct initializer_struct{
settings_struct settings;
initializer_struct(){
if(!isInit){
std::cout << "Doing initialization\n";
settings.load();
isInit = true;
}
// settings.load();
}//====================
~initializer_struct(){
if(isInit){
std::cout << "Doing closing ops\n";
isInit = false;
}
}
};
static initializer_struct init; // static declaration: only create one!
#endif
Мое намерение с этим заголовком, чтобы создать initializer_struct
объект раз; при его создании эта структура выполняет несколько действий, которые устанавливают флаги и настройки для всей библиотеки. Одним из таких действий является создание структуры настроек, которая загружает настройки из файла XML; это действие также должно происходить только один раз, когда построена структура init, поэтому переменные (здесь path
) сохраняются из файла настроек. Заголовок super.hpp
включен во все объекты библиотеки, потому что разные объекты используются в разных емкостях, т. Е. Нет способа предсказать, какие из них будут использоваться в приложении, поэтому я включаю заголовок super.hpp
во всех них, чтобы гарантировать, что это независимо от того, какие объекты используются.
Моя проблема заключается в следующем: когда я включаю super.hpp
в нескольких классов/объектов, которые все нагружены основным приложением, то структура init
, кажется, повторно инициализировать и переменные устанавливается, когда settings_struct
построен перезаписываются по умолчанию значения. Чтобы увидеть это в действии, рассмотрим эти дополнительные файлы:
test.cpp
#include "classA.hpp"
#include "classB.hpp"
#include <iostream>
int main(int argc, char *argv[]){
(void) argc;
(void) argv;
classA a;
classB b;
std::cout << "Settings path = " << init.settings.path << std::endl;
std::cout << "Class A Number = " << a.getNumber() << std::endl;
std::cout << "Class B Number = " << b.getInteger() << std::endl;
}
classA.hpp
#ifndef H_CLASSA
#define H_CLASSA
class classA{
private:
double number;
public:
classA() : number(7) {}
double getNumber();
};
#endif
classA.cpp
#include "super.hpp"
#include "classA.hpp"
double classA::getNumber(){ return number; }
classB.hpp
#ifndef H_CLASSB
#define H_CLASSB
class classB{
private:
int number;
public:
classB() : number(3) {}
int getInteger();
};
#endif
classB.cpp
#include "super.hpp"
#include "classB.hpp"
int classB::getInteger(){ return number; }
Чтобы скомпилировать и запустить пример,
g++ -std=c++11 -W -Wall -Wextra -Weffc++ -pedantic -c classA.cpp -o classA.o
g++ -std=c++11 -W -Wall -Wextra -Weffc++ -pedantic -c classB.cpp -o classB.o
g++ -std=c++11 -W -Wall -Wextra -Weffc++ -pedantic classA.o classB.o test.cpp -o test.out
./test.out
Я ожидаю, что выход из test.out быть следующие:
Doing initialization
Settings path = bar
Number = 7
Doing closing ops
Однако, когда я запускаю это, вместо этого я получаю «Настройки path = foo». Таким образом, мой вывод состоит в том, что initializer_struct
, init
, строится более одного раза. В первый раз логическое значение isInit
является ложным, а структура настроек load
выполняет функции path
в «bar». Для всех последующих инициализаций, isInit
верно, так что функция load
не вызывается снова, и кажется, что значения переменных из неинициализированных settings
(т.е. path = "foo"
) перезаписывает ранее загруженные значения, следовательно, выходной сигнал init.settings.path
в test.cpp
.
Почему это? Почему объект init
построен каждый раз, когда заголовок включен? Я бы подумал, что включенные охранники будут держать код заголовка несколько раз. Если я сделаю переменную init
в переменной test.hpp
нестатической, то создаются несколько копий, а на выходе выводятся несколько итераций «Выполнение инициализации» и «Выполнение операций закрытия». Кроме того, если я раскомментирую вызов функции settings.load()
за пределами оператора условного выражения в конструкторе initializer_struct()
, тогда на выходе будет указан путь к настройке «bar». Наконец, удаление включения super.hpp
из classA.cpp
приводит к значению пути «bar», что подтверждает мою гипотезу о том, что множественные включения test.hpp
приводят к многочисленным вызовам конструктора.
Хотелось бы избежать settings.load()' called for every object that includes
super.hpp` - поэтому я поместил команду в условный оператор. Есть предположения? Как я могу убедиться, что файл настроек читается только один раз и что загруженные значения не перезаписаны? Является ли это абсолютно тупым методом для установки некоторых флагов и настроек, которые использует моя библиотека? Если да, есть ли у вас какие-либо предложения, чтобы упростить процесс и/или сделать более элегантным?
Спасибо!
Edit: Обновленный, чтобы включать два класса объектов, чтобы ближе представлять мою более сложные настройки
Я немного обернулся; Я должен объявлять глобальные объекты статическими членами класса? Если я сделаю их членами класса, разве они перестанут быть глобальными? – AndrewCox
Нет, это неклассические члены класса, которые являются частью каждого экземпляра каждого класса. Элементы класса 'static' являются глобальными. Посмотрите свою любимую книгу на C++ для более подробного обсуждения членов класса 'static' и правильного способа объявить и определить их. –
Ах, конечно, конечно. Но это все еще вызывает вопрос, в каком классе я бы включил их?Красота (ну, почти, поскольку она не совсем работает) текущей реализации заключается в том, что мне не нужно создавать экземпляр определенного объекта «инициализации» в каждой функции «main». – AndrewCox