私はオブジェクトと関数のライブラリを開発しており、super.hpp
いくつかの初期化タスクを含むヘッダー ファイル (ここでは という名前) を持っています。
スーパー.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
オブジェクトを一度作成することです。この構造体が構築されると、ライブラリ全体のフラグと設定を設定するいくつかのアクションが実行されます。これらのアクションの 1 つは、XML ファイルから設定をロードする設定構造体の作成です。このアクションは、init 構造体が構築されるときに 1 回だけ発生する必要があるため、変数 (ここではpath
) は設定ファイルから保存されます。super.hpp
さまざまなオブジェクトがさまざまな容量で使用されるため、ヘッダーはライブラリ内のすべてのオブジェクトに含まれています。つまり、アプリケーションでどのオブジェクトが使用されるかを予測する方法がないため、no と呼ばれることを保証するために、それらすべてにヘッダーを含めます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
ただし、これを実行すると、代わりに「Settings path = foo」が表示されます。したがって、私の結論は、initializer_struct
、init
、が複数回構築されているということです。最初はブール値isInit
が false で、設定構造load
関数path
が「bar」に設定されます。以降のすべての初期化でisInit
は は true であるため、関数は再度呼び出されず、初期化されていない(つまり)load
からの変数値が以前に読み込まれた値を上書きするように見えるため、inの出力になります。settings
path = "foo"
init.settings.path
test.cpp
どうしてこれなの?init
ヘッダーが含まれるたびにオブジェクトが構築されるのはなぜですか? インクルード ガードは、ヘッダー コードが複数回呼び出されるのを防ぐと考えていたでしょう。非静的変数で変数を作成すると、複数のコピーが作成され、出力には「初期化の実行」と「終了操作の実行」の複数の反復が出力されinit
ます。さらに、コンストラクターの条件ステートメントの外側で関数呼び出しのtest.hpp
コメントを外すと、出力に「bar」の設定パスが表示されます。最後に、fromのインクルードを削除すると、"bar" のパス値が得られます。これは、複数の のインクルージョンが複数のコンストラクター呼び出しにつながるという私の仮説をさらに裏付けています。settings.load()
initializer_struct()
super.hpp
classA.cpp
test.hpp
settings.load()' called for every object that includes
super.hpp`は避けたいので、コマンドを条件ステートメント内に配置しました。何かご意見は?設定ファイルが 1 回だけ読み込まれ、読み込まれた値が上書きされないようにするにはどうすればよいですか? これは、ライブラリが使用するいくつかのフラグと設定を設定するための完全に鈍い方法ですか? もしそうなら、プロセスをよりシンプルかつ/またはよりエレガントにするための提案はありますか?
ありがとう!
編集:より複雑な設定をより正確に表すために、2 つのオブジェクト クラスを含めるように更新されました。