私はほとんどCでしか作業しておらず、C++でなじみのない問題に直面しています。
Cには次のような関数があり、非常に典型的だとしましょう。
int some_c_function(const char* var)
{
if (var == NULL) {
/* Exit early so we don't dereference a null pointer */
}
/* The rest of the code */
}
そして、C++で同様の関数を記述しようとしているとしましょう。
int some_cpp_function(const some_object& str)
{
if (str == NULL) // This doesn't compile, probably because some_object doesn't overload the == operator
if (&str == NULL) // This compiles, but it doesn't work, and does this even mean anything?
}
基本的に、私がやろうとしているのは、some_cpp_function()がNULLで呼び出されたときにプログラムがクラッシュするのを防ぐことです。
オブジェクトC++でこれを行う最も一般的/一般的な方法は何ですか(==
演算子)?
これは正しいアプローチですか?つまり、オブジェクトを引数として取る関数を書くべきではなく、メンバー関数を書くべきでしょうか? (ただし、そうであっても、元の質問に答えてください)
オブジェクトへの参照をとる関数、またはオブジェクトへのCスタイルポインターをとる関数の間に、一方を選択する理由はありますか?
基本的に、私がやろうとしているのは、some_cpp_function()がNULLで呼び出されたときにプログラムがクラッシュするのを防ぐことです。
NULLで関数を呼び出すことはできません。参照を持つ目的の1つは、定義するときに初期化する必要があるため、常に何らかのオブジェクトを指します。参照は派手なポインターとは思わず、オブジェクト自体のエイリアス名と考えてください。そうすれば、この種の混乱は生じません。
参照をNULLにすることはできません。このインターフェースにより、実際のオブジェクトを関数に渡すことができます。
したがって、NULLをテストする必要はありません。これが、参照がC++に導入された理由の1つです。
ポインターを受け取る関数を作成できることに注意してください。この状況でも、NULLをテストする必要があります。値がNULLの場合、Cの場合と同様に早期に戻ります。注:ポインターがNULLの場合は、例外を使用しないでください。パラメーターを決してNULLにしない場合は、参照を使用するインターフェースを作成します。
誰もが言ったように、参照はnullにはできません。これは、参照がオブジェクトを参照するためです。あなたのコードで:
// this compiles, but doesn't work, and does this even mean anything?
if (&str == NULL)
オブジェクトstr
のアドレスを取得しています。定義により、str
は存在するため、アドレスがあります。したがって、NULL
にはできません。したがって、構文的には上記は正しいですが、論理的には、if
条件は常にfalseになります。
あなたの質問について:あなたが何をしたいかに依存します。関数で引数を変更できるようにしますか?はいの場合、参照を渡します。そうでない場合は、しないでください(またはconst
への参照を渡します)。詳細については、 このC++ FAQ を参照してください。
一般に、C++では、ほとんどの人は参照渡しよりも参照渡しの方が優先されます。その理由の1つは、まさにあなたが発見したことです。参照をNULL
にすることはできないため、関数内で参照を確認するという頭痛の種を回避できます。
C++参照はポインターでもJava/C#スタイルの参照でもないため、NULLにすることはできません。それらは、別の既存オブジェクトのエイリアスのように動作します。
場合によっては、コードにバグがある場合、すでに死んでいるか存在しないオブジェクトへの参照を取得するかもしれませんが、あなたができる最善のことは、プログラムがすぐに死んで、何が起こったのかをデバッグできるようにすることですプログラムが破損した理由。
つまり、「null参照」のコードチェックはif ( &reference == 0 )
のようになりますが、整形式プログラムにnull参照が存在できないことは標準で明確です。参照がnullオブジェクトにバインドされている場合、プログラムは不正な形式であり、修正する必要があります。オプションの値が必要な場合は、ポインター(またはboost::optional
)、参照ではありません。
次のように、参照の場合、特別な指定オブジェクトをnullオブジェクトとして使用できます。
class SomeClass
{
public:
int operator==(SomeClass &object)
{
if(this == &object)
{
return true;
}
return false;
}
static SomeClass NullObject;
};
SomeClass SomeClass::NullObject;
void print(SomeClass &val)
{
if(val == SomeClass::NullObject)
{
printf("\nNULL");
}
else
{
printf("\nNOT NULL");
}
}
- オブジェクトC++でこれを行う最も一般的/一般的な方法は何ですか(==演算子のオーバーロードを必要としません)?
- これは正しいアプローチですか?すなわち。オブジェクトを引数として取る関数を書くべきではなく、メンバー関数を書くべきですか? (ただし、そうであっても、元の質問に答えてください。)
いいえ、参照をnullにすることはできません(未定義の動作が既に発生していない限り、この場合、すべてのベットはすでにオフになっています)。メソッドを書くべきか非メソッドを書くべきかは、他の要因に依存します。
- オブジェクトへの参照をとる関数、またはオブジェクトへのCスタイルポインターをとる関数の間に、一方を選択する理由はありますか?
「オブジェクトなし」を表す必要がある場合は、関数にポインターを渡し、そのポインターをNULLにします。
int silly_sum(int const* pa=0, int const* pb=0, int const* pc=0) {
/* Take up to three ints and return the sum of any supplied values.
Pass null pointers for "not supplied".
This is NOT an example of good code.
*/
if (!pa && (pb || pc)) return silly_sum(pb, pc);
if (!pb && pc) return silly_sum(pa, pc);
if (pc) return silly_sum(pa, pb) + *pc;
if (pa && pb) return *pa + *pb;
if (pa) return *pa;
if (pb) return *pb;
return 0;
}
int main() {
int a = 1, b = 2, c = 3;
cout << silly_sum(&a, &b, &c) << '\n';
cout << silly_sum(&a, &b) << '\n';
cout << silly_sum(&a) << '\n';
cout << silly_sum(0, &b, &c) << '\n';
cout << silly_sum(&a, 0, &c) << '\n';
cout << silly_sum(0, 0, &c) << '\n';
return 0;
}
「オブジェクトなし」を表す必要がない場合、参照は正常に機能します。実際、演算子のオーバーロードは、オーバーロードを取るため、はるかに簡単です。
Boost :: optionalのようなものを使用できます。
NULLはポインターでのみ使用する必要があります。関数は参照を受け入れ、NULLにすることはできません。
Cで記述するのと同じように関数を記述します。
C++参照は当然nullにはできません。チェックする必要はありません。この関数は、既存のオブジェクトへの参照を渡すことによってのみ呼び出すことができます。