我正在开发一个对象和函数库,并且有一个头文件,这里的头文件名为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
但是,当我运行这个,我反而得到“设置路径=富”。因此,我的结论是initializer_struct
,init
,不止一次构建。第一次,布尔型isInit
为false,设置结构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 includes
super.hpp` - 这就是为什么我将该命令放在条件语句中的原因。有什么想法吗?如何确保设置文件只读一次,并且加载的值不会被覆盖?这是一个完全钝的方法来设置我的图书馆使用的一些标志和设置?如果是这样,你有任何建议,使过程更简单和/或更优雅?
谢谢!
编辑:更新以包括两个对象类,以接近代表我更复杂的设置
我有点转身;我应该将全局对象声明为哪个类的静态成员?如果我让他们成为班级成员,他们是不是不再是全球性的? – AndrewCox
不,这是非'静态'类成员,它是每个类的每个实例的一部分。静态类成员是全球性的。在C++中查看您最喜欢的书籍,以更深入地讨论'static'类成员以及声明和定义它们的正确方法。 –
啊,对,当然。但是,这仍然会引发一个问题,我会让他们成为什么班级的成员?当前实现的美观(当然,因为它不起作用)是我不必在每个'main'函数中实例化一个特定的“初始化”对象。 – AndrewCox