web-dev-qa-db-ja.com

C ++で参照渡しよりもポインタ渡しの方がメリットはありますか?

C++で参照渡しよりもポインタ渡しの利点は何ですか?

最近、参照渡しではなくポインタによる関数引数の受け渡しを選択するいくつかの例を見てきました。これを行うメリットはありますか?

例:

func(Sprite *x);

の呼び出しで

func(&mySprite);

vs.

func(Sprite &x);

の呼び出しで

func(mySprite);
215
Matt Pascoe

ポインターはNULLパラメーターを受け取ることができますが、参照パラメーターは受け取ることができません。 「オブジェクトなし」を渡したい場合は、参照の代わりにポインターを使用してください。

また、ポインタで渡すことにより、オブジェクトが値で渡されるか参照で渡されるかを呼び出しサイトで明示的に確認できます。

// Is mySprite passed by value or by reference?  You can't tell 
// without looking at the definition of func()
func(mySprite);

// func2 passes "by pointer" - no need to look up function definition
func2(&mySprite);
209
Adam Rosenfield

ポインターで渡す

  • 発信者はアドレスを取得する必要があります->透過的ではありません
  • nothingを意味する0の値を提供できます。これは、オプションの引数を提供するために使用できます。

参照渡し

  • 呼び出し元はオブジェクトを渡すだけ->透明。ポインター型のオーバーロードは不可能であるため、オペレーターのオーバーロードに使用する必要があります(ポインターは組み込み型です)。したがって、ポインタを使用してstring s = &str1 + &str2;を実行することはできません。
  • 0の値は不可能->呼び出された関数はそれらをチェックする必要がない
  • Constへの参照は一時的なものも受け入れます:void f(const T& t); ... f(T(a, b, c));、一時的なアドレスを取得できないため、そのようなポインターは使用できません。
  • 最後になりましたが、参照は使いやすい->バグの可能性が少なくなります。
232

アレンホルブの「足で自分を撃つには十分なロープ」には、次の2つのルールがリストされています。

120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers

彼は、参照がC++に追加された理由をいくつか挙げています。

  • コピーコンストラクタを定義するために必要です
  • オペレーターのオーバーロードに必要です
  • const参照により、コピーを回避しながら値渡しのセマンティクスを使用できます

彼の主なポイントは、参照が「出力」パラメーターとして使用されるべきではないということです。呼び出しサイトでは、パラメーターが参照パラメーターであるか値パラメーターであるかが示されないためです。したがって、彼のルールは、引数としてconst参照のみを使用することです。

個人的には、パラメータが出力パラメータであるかどうかをより明確にするため、これは良い経験則だと思います。ただし、個人的にはこれに全般的に同意しますが、出力パラメーターを参照として主張する場合、チームの他のユーザーの意見に左右されることを許可します(一部の開発者は非常に気に入っています)。

61
Michael Burr

「cplusplus.com」の記事による推論が好きです。

  1. 関数がパラメーターを変更することを望まず、値のコピーが簡単な場合(int、double、char、boolなど...単純型。std:: string、std :: vector、および他のすべてのSTLコンテナは単純なタイプではありません。)

  2. 値のコピーに時間がかかり、かつ関数がAND NULLを指す値を変更したくない場合、constポインターを渡します。これは、関数が処理する有効な期待値です。

  3. 値のコピーに時間がかかり、かつ関数がAND NULLを指す値を変更したい場合は、非定数ポインターを渡します。これは、関数が処理する有効な期待値です。

  4. 値のコピーに時間がかかり、関数が参照される値を変更したくない場合、const参照を渡します。ポインタが代わりに使用された場合、NULLは有効な値ではありません。

  5. 値のコピーに時間がかかり、関数が参照する値を変更する場合、非連続参照を渡します。ポインタが代わりに使用された場合、NULLは有効な値ではありません。

  6. テンプレート関数を作成する場合、この議論の範囲外であると考えるいくつかのトレードオフがあるため、明確な答えはありませんが、ほとんどのテンプレート関数は値または(定数)参照によってパラメータを取ると言えば十分ですただし、イテレータの構文はポインタの構文(「参照解除」へのアスタリスク)に類似しているため、イテレータを引数として期待するテンプレート関数もデフォルトでポインタを受け入れます(NULLイテレータの概念には異なる構文があるため、NULLをチェックしません) )。

http://www.cplusplus.com/articles/z6vU7k9E/

これから私が取るのは、ポインターまたは参照パラメーターを使用することを選択することの主な違いは、NULLが許容値であるかどうかです。それでおしまい。

値が入力、出力、変更可能などであるかどうかは、結局、関数に関するドキュメント/コメントにあるべきです。

60
R. Navega

上記の投稿の説明:


参照はではなく null以外のポインターを取得することを保証します。 (多くの場合、それらをそのように扱います。)

恐ろしく悪いコードですが、woodshedbadコードの背後に連れて行って、次のコードをコンパイルして実行します:(少なくとも私のコンパイラの下では)

bool test( int & a)
{
  return (&a) == (int *) NULL;
}

int
main()
{
  int * i = (int *)NULL;
  cout << ( test(*i) ) << endl;
};

参照に関する実際の問題は他のプログラマーにあり、今後はIDIOTSと呼ばれ、コンストラクターで割り当てを解除しますデストラクタではであり、コピーコンストラクタまたはoperator =()。の提供に失敗します

突然、foo(BAR bar)foo(BAR の間に世界の違いがあります=バー)。 (自動ビット単位コピー操作が呼び出されます。デストラクタの割り当て解除は2回呼び出されます。)

ありがたいことに、最新のコンパイラは、同じポインタのこの二重の割り当て解除を選択します。 15年前はそうではありませんでした。 (gcc/g ++では、setenv MALLOC_CHECK_ 0を使用して古い方法を再検討します。)DEC UNIXでは、同じメモリが2つの異なるメモリに割り当てられます。オブジェクト。デバッグがたくさんあります...


より実際的に:

  • 参照は、他の場所に保存されているデータを変更していることを隠します。
  • 参照とコピーされたオブジェクトを混同するのは簡単です。
  • ポインターはそれを明らかにします!
8
Mr.Ree

あんまり。内部的には、参照渡しは基本的に参照オブジェクトのアドレスを渡すことで実行されます。そのため、ポインターを渡すことによる効率の向上は実際にはありません。

ただし、参照渡しには1つの利点があります。渡されるオブジェクト/タイプのインスタンスを持つことが保証されます。ポインターを渡すと、NULLポインターを受け取るリスクが発生します。参照渡しを使用すると、暗黙的なNULLチェックを1レベル上に関数の呼び出し元にプッシュできます。

3
Brian