これは重複するようですが、たぶんそれは明らかにされていないので...
これは、変数(ポインターではない)がC++クラスで初期化されているかどうかを確認する適切な方法ですか?
class MyClass
{
void SomeMethod();
char mCharacter;
double mDecimal;
};
void MyClass::SomeMethod()
{
if ( mCharacter )
{
// do something with mCharacter.
}
if ( ! mDecimal )
{
// define mDecimal.
}
}
変数の内容が未定義であるかどうかを確認する方法はありません。あなたができる最善のことは、さらに初期化を実行する必要があることを示すために、シグナル/センチネルの値を割り当てることです(例えば、コンストラクターで)。
定義されていない変数は、コンパイルエラーの原因になります。
あなたが求めているのは、それが初期化済みかどうかをチェックすることです。ただし、初期化は単なる値であり、コンストラクタで選択して割り当てる必要があります。
例えば:
class MyClass
{
MyClass() : mCharacter('0'), mDecimal(-1.0){};
void SomeMethod();
char mCharacter;
double mDecimal;
};
void MyClass::SomeMethod()
{
if ( mCharacter != '0')
{
// touched after the constructor
// do something with mCharacter.
}
if ( mDecimal != -1.0 )
{
// touched after the constructor
// define mDecimal.
}
}
もちろん、ロジックのコンテキストで何かを意味するデフォルト値に初期化する必要があります。
アプリケーションに応じて(特に、すでにboostを使用している場合)、 boost::optional
を調べてください。
(更新:C++ 17の時点で、オプションは std::optional
)のように標準ライブラリの一部になりました
探しているプロパティがあり、スロットが実際に値を保持しているかどうかを追跡します。デフォルトでは、値を保持せずfalseと評価されるように構築されていますが、trueと評価された場合は、その値を逆参照してラップされた値を取得できます。
class MyClass
{
void SomeMethod();
optional<char> mCharacter;
optional<double> mDecimal;
};
void MyClass::SomeMethod()
{
if ( mCharacter )
{
// do something with *mCharacter.
// (note you must use the dereference operator)
}
if ( ! mDecimal )
{
// call mDecimal.reset(expression)
// (this is how you assign an optional)
}
}
その他の例は The Boost documentation にあります。
C++ 17では、 std::optional
を使用して、変数が初期化されているかどうかを確認できます。
#include <optional>
#include <iostream> // needed only for std::cout
int main() {
std::optional<int> variable;
if (!variable) {
std::cout << "variable is NOT initialized\n";
}
variable = 3;
if (variable) {
std::cout << "variable IS initialized and is set to " << *variable << '\n';
}
return 0;
}
これにより、出力が生成されます。
variable is NOT initialized
variable IS initialized and is set to 3
提供したコードでstd::optional
を使用するには、 <optional>
標準ライブラリヘッダーを含め、それぞれの変数宣言にstd::optional<...>
を追加する必要があります。
#include <optional>
class MyClass
{
void SomeMethod();
std::optional<char> mCharacter;
std::optional<double> mDecimal;
};
void MyClass::SomeMethod()
{
if ( mCharacter )
{
std::cout << *mCharacter; // do something with mCharacter.
}
if ( ! mDecimal )
{
mDecimal = 3.14159; // define mDecimal.
}
}
C++-11では、スマートポインターを使用して変数を保存することを検討できます。 toString()
の動作がbar
が初期化されているかどうかに依存するこのMVEを考えてみましょう。
#include <memory>
#include <sstream>
class Foo {
private:
std::shared_ptr<int> bar;
public:
Foo() {}
void setBar(int bar) {
this->bar = std::make_shared<int>(bar);
}
std::string toString() const {
std::ostringstream ss;
if (bar) // bar was set
ss << *bar;
else // bar was never set
ss << "unset";
return ss.str();
}
};
このコードを使用する
Foo f;
std::cout << f.toString() << std::endl;
f.setBar(42);
std::cout << f.toString() << std::endl;
出力を生成します
unset
42
デフォルトでは、変数(またはポインター)が初期化されているかどうかはわかりません。ただし、他の全員が「簡単」または「通常」のアプローチを教えているので、他に考えるべきことを紹介します。 couldそのようなことを追跡する方法は次のとおりです(いいえ、私は個人的にこれを行うことは決してありませんが、おそらく私とは異なるニーズがあります)。
class MyVeryCoolInteger
{
public:
MyVeryCoolInteger() : m_initialized(false) {}
MyVeryCoolInteger& operator=(const int integer)
{
m_initialized = true;
m_int = integer;
return *this;
}
int value()
{
return m_int;
}
bool isInitialized()
{
return m_initialized;
}
private:
int m_int;
bool m_initialized;
};
値が初期化されているかどうかを確認する合理的な方法はありません。
何かが初期化されているかどうかを気にする場合、それをチェックするのではなく、コンストラクターにコードを入れて、それらが常に初期化され、処理されるようにします。
MyClass
はPODクラスタイプであるため、MyClass
の非静的インスタンスを作成すると、これらの非静的データメンバーは不定の初期値を持ちます。そのため、これは有効な方法ではありませんそれらが特定の非ゼロ値に初期化されているかどうかを確認してください...あなたは基本的にそれらがゼロで初期化されると仮定していますが、コンストラクターで値を初期化していないためそうではありません。
クラスの非静的データメンバーをゼロで初期化する場合は、初期化リストとクラスコンストラクターを作成することをお勧めします。例えば:
class MyClass
{
void SomeMethod();
char mCharacter;
double mDecimal;
public:
MyClass();
};
MyClass::MyClass(): mCharacter(0), mDecimal(0) {}
上記のコンストラクタの初期化リストは、データメンバーをゼロに値で初期化します。 mCharacter
およびmDecimal
のゼロ以外の値は、コードのどこかで明確に設定されている必要があり、適切に操作できるゼロ以外の値が含まれていると正しく仮定できます。
メンバー変数が初期化されているかどうかを確認する方法を意味する場合は、コンストラクターでセンチネル値を割り当てることでこれを行うことができます。その変数の通常の使用では決して発生しない値として、センチネル値を選択します。変数の範囲全体が有効と見なされる場合、初期化されたかどうかを示すブール値を作成できます。
#include <limits>
class MyClass
{
void SomeMethod();
char mCharacter;
bool isCharacterInitialized;
double mDecimal;
MyClass()
: isCharacterInitialized(false)
, mDecimal( std::numeric_limits<double>::quiet_NaN() )
{}
};
void MyClass::SomeMethod()
{
if ( isCharacterInitialized == false )
{
// do something with mCharacter.
}
if ( mDecimal != mDecimal ) // if true, mDecimal == NaN
{
// define mDecimal.
}
}
アサーションで変数を参照してから、-fsanitize=address
:
void foo (int32_t& i) {
// Assertion will trigger address sanitizer if not initialized:
assert(static_cast<int64_t>(i) != INT64_MAX);
}
これにより、プログラムはスタックトレースで確実にクラッシュします(未定義の動作とは対照的に)。
C++言語では、変数が初期化されているかどうかを確認する方法はありません(ただし、コンストラクターを持つクラス型は自動的に初期化されます)。
代わりに、クラスを有効な状態に初期化するコンストラクターを提供する必要があります。静的コードチェッカー(および場合によっては一部のコンパイラ)は、コンストラクターで不足している変数を見つけるのに役立ちます。この方法では、haveが偽の状態になることを心配せず、メソッドのif
チェックが完全になくなる可能性があります。
たとえば、charsの代わりに文字列を使用する場合、次のようなことができるかもしれません。
//a is a string of length 1
string a;
//b is the char in which we'll put the char stored in a
char b;
bool isInitialized(){
if(a.length() != NULL){
b = a[0];
return true;
}else return false;
}