【发布时间】:2016-08-13 23:57:56
【问题描述】:
我正在开发一个对象和函数库,并有一个头文件,这里命名为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
但是,当我运行它时,我得到的是“设置路径 = foo”。因此,我的结论是initializer_struct、init 不止一次被构建。第一次,布尔值isInit 为假,设置结构load 函数将path 设置为“bar”。对于所有后续初始化,isInit 为真,因此不会再次调用 load 函数,并且似乎来自未初始化 settings(即path = "foo")的变量值会覆盖先前加载的值,因此输出init.settings.path 中的 test.cpp。
这是为什么?为什么每次包含标头时都会构造init 对象?我原以为包含守卫会阻止标题代码被多次调用。如果我将test.hpp 中的init 变量设为非静态变量,则会创建多个副本,并且输出会打印“正在执行初始化”和“正在执行关闭操作”的多次迭代。此外,如果我在initializer_struct() 构造函数的条件语句之外取消注释settings.load() 函数调用,则输出会给出“bar”的设置路径。最后,从classA.cpp 中删除包含super.hpp 会导致路径值为“bar”,这进一步支持了我的假设,即多次包含test.hpp 会导致多次构造函数调用。
我想避免使用settings.load()' called for every object that includessuper.hpp` - 这就是我将命令放在条件语句中的原因。有什么想法吗?如何确保设置文件只读取一次并且加载的值不会被覆盖?这是设置我的库使用的一些标志和设置的完全钝的方法吗?如果是这样,您有什么建议可以让流程更简单和/或更优雅?
谢谢!
编辑:更新为包含两个对象类以更接近地代表我更复杂的设置
【问题讨论】:
标签: c++ struct header inclusion