我正在開發一個對象和函數庫,並且有一個頭文件,這裏的頭文件名爲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