クラスに次のようなメンバー関数があります。
int MyClass::m_Func(int& val);
その中で、私は何らかの操作を行い、結果を に入れval
ます。そして、操作の結果に応じて、関数からさまざまな値を返しました。同様に、成功した場合、0
またはエラーが発生した場合は他の値を返します。
私の友人の 1 人が、変数の参照をメンバー関数に渡すのは良い習慣ではないと言っていました。本当ですか?はいの場合、なぜそうですか?
クラスに次のようなメンバー関数があります。
int MyClass::m_Func(int& val);
その中で、私は何らかの操作を行い、結果を に入れval
ます。そして、操作の結果に応じて、関数からさまざまな値を返しました。同様に、成功した場合、0
またはエラーが発生した場合は他の値を返します。
私の友人の 1 人が、変数の参照をメンバー関数に渡すのは良い習慣ではないと言っていました。本当ですか?はいの場合、なぜそうですか?
このプラクティスがやや疑わしい主な理由であり、私が一般的に推奨しない理由は、ユーザーが関数を呼び出すときに、
int val;
obj.my_Func(val);
の値が実際に変更されているという事実に気付いていない可能性がありますval
。これは、関数にパラメーターが 1 つしかなく、エラー コード以外の戻り値がない場合は多かれ少なかれ明白かもしれませんが、もう少し複雑な関数呼び出しがあるとすぐに、たとえば、
int x,y,rad,len;
obj.calculate_coord(x,y,rad,len);
意図は、rad
andlen
からx
andを計算することy
、またはその逆、またはまったく異なるものを計算することです。引数名はユーザーによって選択され、その順序または型は、どれが入力として機能し、どれが出力パラメーターとして機能するかについてのヒントを提供しないため、これにより誤解が生じる可能性があり、コードの保守が難しくなり、バグが発生する可能性があります.
一方、C++ 構文を使用して、値やエラー コードだけでなく、1 つ以上の値とエラー コードの組み合わせを適切に返すことは非常に簡単です。
最も単純なケースでは、次のような関数を使用できます (C++11 構文を使用):
std::tuple<int,int,int> my_Func(int input)
{
/* calculate.. */
return { error_code , val1 , val2 };
}
2 つの値とエラー コードを返します。input
これが純粋な入力パラメーター (値渡し) であり、戻り値が複雑であり、戻りたいものすべてが含まれていることに疑問の余地はありません。
一部のプログラマーが提案するかもしれないことに反して、これは通常、戻り値がはるかに大きい場合でも、多くの不要なコピーを引き起こしません (戻り値の最適化のため、C++11 ではムーブ セマンティクスのため)。
特に C++11 より前の場合、例外が存在する場合があります。つまり、移動セマンティクスを使用できない、または何らかの理由で戻り値の最適化が適用されない特殊なケースです。また、コードのプロファイリングによって、a) 関数が非常に頻繁に使用されている、b) なんらかの理由で参照を介して出力を渡す方が高速であることが明らかになる場合もあります。このような場合、参照によって出力パラメーターを渡すことが適切な手法である可能性があります。
もちろん、上で述べたことは、参照を出力パラメーターとして使用する場合、つまり、ユーザーに値を返す間接的な方法としてのみ適用されます。特に、関数がそのパラメーターの一部を変更するという事実が、そのパラメーターによって明らかにされていない場合に当てはまります。名前。
参照渡しには、他にも多くの有効な理由があります。const 参照による。
あなたの友人は、あなたが選択したエラー報告手法についてではなく、変数への参照をメソッドに渡すことについて実際に不満を持っていなかったと思います(実際、人々は通常、参照によってパラメーターを渡すことをお勧めします。渡すよりもはるかに安全です)ポインタによって)。C++ では、通常、例外を使用してエラーをチェックします。あなたのコードは少しCスタイルです。したがって、代わりに:
int res;
myInstance->m_Func(res);
if (res != 0)
// do sth
私はむしろ書きたい:
MyClass::m_Func()
{
if (some_condition)
throw std::exception("Error!");
}
// (...)
try
{
myInstance->m_Func();
}
catch (...)
{
// do sth
}
免責事項:これはスタイル、慣習などの問題であるため、これについては意見が無意味に異なる場合があります.
そこにあるのは、いわゆる「出力パラメーター」です。つまり、関数は戻り値ではなくパラメーターを介して結果を渡します。これは主に、戻り値がエラー コード (成功インジケータ) で占められているためです。
C などの多くの言語では、これは関数とエラー処理の一般的な使用法です。何らかの理由で例外を使用できない C++ の一部のアプリケーションでは、このスタイルも採用される場合があります。ただし、「純粋な」C++ では、エラー処理に例外があります。関数へのパラメータはすべて入力パラメータであり、関数によって生成された値は戻り値を介して渡されるのが一般的です。返す値が複数ある場合、値はstd::pair
、std::tuple
または何らかの構造体にまとめられます。
おそらく、失敗は実際には例外的なケースではなく、関数の一般的で予想される結果であるためです。その場合、値のペア (1 つの成功インジケータと 1 つの結果値) を返します。それらを一緒にバンドルすることは最初は奇妙に見えるかもしれませんが、とにかく両方を評価する必要があるため、実行可能な解決策です。ただし、ほとんどの場合、関数がそのタスクを実行できない場合、つまり有用な戻り値を計算できない場合は、例外をスローする方がクリーンになることを覚えておいてください。