実行時にクラスの静的constメンバーを初期化することは可能ですか?この変数はプログラム全体で定数ですが、コマンドライン引数として送信したいと思います。
//A.h
class A {
public:
static const int T;
};
//in main method
int main(int argc,char** argv)
{
//how can I do something like
A::T = atoi(argv[1]);
}
これが実行できない場合、使用する必要がある変数のタイプは何ですか?実行時に初期化し、定数プロパティを保持する必要があります。
コンパイル時ではなくプログラムの起動時にstatic const
シンボルを初期化することは不可能であるというコメントと回答に同意できず、申し訳ありません。
実際、これはIS可能で、何度も使用しましたが、構成ファイルから初期化しました。以下のようなものです。
// GetConfig is a function that fetches values from a configuration file
const int Param1 = GetConfig("Param1");
const int MyClass::Member1 = GetConfig("MyClass.Member1");
ご覧のとおり、これらの静的定数は必ずしもコンパイル時に認識されているわけではありません。これらは、構成ファイルなどの環境から設定できます。
一方で、それらをargv []から設定することは、可能であれば、非常に難しいように見えます。これは、main()の開始時に静的シンボルがすでに初期化されているためです。
main
の翻訳単位の静的な初期化はstatic
が取得される前に行われるため、main
変数の初期化はmain
の開始後に生成されたデータに依存できません。制御、および他の翻訳単位での静的初期化は、main
翻訳単位の静的初期化の前または後に、不特定の順序で発生する可能性があります。
ただし、次のように非表示の非const変数を初期化して、const
への参照を提供できます。
struct A {
public:
// Expose T as a const reference to int
static const int& T;
};
//in main.cpp
// Make a hidden variable for the actual value
static int actualT;
// Initialize A::T to reference the hidden variable
const int& A::T(actualT);
int main(int argc,char** argv) {
// Set the hidden variable
actualT = atoi(argv[1]);
// Now the publicly visible variable A::T has the correct value
cout << A::T << endl;
}
いいえ、それはできません。
これが実行できない場合、使用する変数のタイプは何ですか?
const
以外のメンバーを使用できます。
class A
{
public:
static int T;
};
int A::T;
別のオプションは、T
をプライベートメンバーにし、main
をフレンドにして、値を変更できるようにし、関数を介してメンバーを公開することです。
#include <cstdlib>
class A
{
public:
static int getT() { return T; }
private:
static int T;
friend int main(int argc, char** argv);
};
int A::T;
int main(int argc, char** argv)
{
A::T = std::atoi(argv[1]);
return 0;
}
できないだけでなく、const_castをいじってこれを試してはいけません。静的constメンバーは、読み取り専用セグメントになる可能性が非常に高く、それらを変更しようとすると、プログラムがクラッシュします。
通常、複数の構成値があります。したがって、それらを構造体に入れてください。構造体への通常のグローバルアクセスはconstです。
_const config* Config;
...
main (int argc, char* argv [])
{
Config= new config (argc, argv);
...
}
_
より洗練されたものになり、グローバルfunctionを使用して構成を返すことができるため、通常のコードではポインターを変更することもできませんが、誤って変更することは困難です。
ヘッダーファイルは、すべてのユーザーがget_config ()
を公開しますが、設定方法は、そうすることを意図したコードにのみ知られています。
いいえ、変数をstaticおよびconstとして定義したため、その値を変更することはできません。その値は定義自体に設定するか、クラスAのオブジェクトを作成するときに呼び出されるコンストラクターを介して設定する必要があります。
const
への参照を提供します(dasblinkenlightで示されています)。class A {
public:
static const int &T;
};
static int dummy = 0;
const int &A::T = dummy;
int main() {
dummy = 10;
std::cout << A::T << std::endl;
}
const
以外の静的メンバーを使用する(R Sahuが示すように):class A {
public:
static int T;
};
int A::T = 0;
int main() {
A::T = 10;
}
class A {
friend void foo(int);
static int dummy;
public:
static const int &T;
};
const int &A::T = A::dummy;
int A::dummy = 0;
void foo(int val) { A::dummy = val; }
int main() {
foo(10);
std::cout << A::T << std::endl;
}
class A {
static int dummy;
public:
static const int &T;
static void foo(int val) { A::dummy = val; }
};
const int &A::T = A::dummy;
int A::dummy = 0;
int main() {
A::foo(10);
std::cout << A::T << std::endl;
}
一度だけ初期化したい場合は、ヘルパー関数を次のように変更できます。
static void foo(int val) {
static bool init = true;
if(init) A::dummy = val;
init = false;
}
最近私も同じ問題に直面していたので、@ A.S.Hの答えが完全に最も近いことがわかりましたが、変数を初期化する必要があるため、いくつかの問題が発生する可能性があります。
argc
やargv
など、まだ利用できないデータソースは使用できません。だから私は次のことを思いつきました:
template <class T>
class StaticConfig
{
public:
StaticConfig()
{
if (!mIsInitialised)
{
throw std::runtime_error("Tried to construct uninitialised StaticConfig!");
}
}
const T*
operator -> () const
{
return &mConfig;
}
private:
friend class ConfigHandler;
StaticConfig(const T& config)
{
mConfig = config;
mIsInitialised = true;
}
static T mConfig;
static bool mIsInitialised;
};
template <class T>
T StaticConfig<T>::mConfig;
template <class T>
bool StaticConfig<T>::mIsInitialised = false;
データは静的ですが、constではないので、すぐに初期化する必要はなく、より適切なタイミングで正しい値を割り当てることができます。読み取り専用アクセスは、operator ->
のオーバーロードを通じて与えられます。デフォルトのコンストラクターは、このタイプのStaticConfig
に有効なデータがすでにロードされているかどうかをチェックし、ロードされていない場合はスローします。これは実際には発生しないはずですが、デバッグの補助として役立ちます。プライベートコンストラクターを使用すると、有効なデータで型をロードできます。データのロードを担当するConfigHandler
クラスがフレンドになり、プライベートコンストラクターにアクセスできるようになります。
ConfigHandler
インスタンスは、すべてのStaticConfig
タイプを初期化するためにすべての依存関係が利用できる適切なタイミングで簡単に作成できます。完了したら、ConfigHandler
インスタンスを破棄できます。その後、クラスは単純に適切なタイプのStaticConfig
をメンバーとして含め、最小限の侵入でデータの読み取り専用アクセスを行うことができます。
トリックがありますが、おそらくそれを避けるべきです!原理を説明するための最低限の例を以下に示します。
int const& foo(int i) {
static const int j = (i == 0 ? throw 0 : i);
return j;
}
int main() {
try {
int x = foo(0); // oops, we throw
} catch(...) {}
int x = foo(1); // initialized..
int y = foo(0); // still works..
}
気をつけて!
N-O
必要なもののセマンティクスはすべて間違っています。そのためにstatic-constを使用しないでください。
staticは、静的ストレージ期間と内部リンケージを持つオブジェクトまたは整数型です。
A const is an object that does not change its value throughout application's lifetime, any attempt to change it results in UD . ( the overwhelming majority of such cases is a pretty well defined crash )
この質問の結果として、暗黙の動作を模倣するために危険な回避策が提案されました。ほとんどの例では、static-const-referenceには、実行時に割り当て可能な何らかの方法で隠されたstaticが与えられます。 これ 。
このようなコードを維持することの難しさは別として、宣言されたセマンティクスが実際に強制されないという問題が残っています。
たとえば、アプリケーションランタイム全体で値constを維持する場合、完全に有効なconst_cast<int &>(A::T) = 42
を実行することでハッキングできます。参照される型はconstではないため、コードを完全に定義します。
ここで求められているものは、アプリケーション全体で一度だけ初期化できるクラスで、内部リンケージがあり、アプリケーションの存続期間があります。
だからそれを行うテンプレートクラスを実行してください:
template<typename V> class fixation
{
bool init = true;
V val;
public:
fixation(V const & v) : init(true), val(v) {}
fixation & operator=( fixation arg)
{
if(init )
{
this->val = arg.val;
}
this->init = false;
return *this;
}
V get()
{
return val;
}
};
struct A
{
static fixation<int> T;
};
2回目に呼び出された場合の処理方法、つまり実装の決定。この例では、値は完全に無視されます。例外をスローしたり、アサーションを実行したりすることもできます。