C++で参照渡しよりもポインタ渡しの利点は何ですか?
最近、関数の引数を参照渡しではなくポインターで渡すことを選択した多くの例を見てきました。これを行う利点はありますか?
例:
func(SPRITE *x);
の呼び出しで
func(&mySprite);
対。
func(SPRITE &x);
の呼び出しで
func(mySprite);
C++で参照渡しよりもポインタ渡しの利点は何ですか?
最近、関数の引数を参照渡しではなくポインターで渡すことを選択した多くの例を見てきました。これを行う利点はありますか?
例:
func(SPRITE *x);
の呼び出しで
func(&mySprite);
対。
func(SPRITE &x);
の呼び出しで
func(mySprite);
nothing
。これは、オプションの引数を提供するために使用できます。string s = &str1 + &str2;
したがって、ポインターを使用することはできません 。void f(const T& t); ... f(T(a, b, c));
のアドレスを取得できないため、ポインタはそのようには使用できません。ポインターは 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);
「cplusplus.com:」の記事の推論が気に入っています。
関数がパラメーターを変更する必要がなく、値が簡単にコピーできる場合 (int、double、char、bool など... 単純な型)、std::string、std::vector、およびその他すべての STL を値渡しします。コンテナーは単純型ではありません)。
値をコピーするのにコストがかかり、関数が指す値を変更したくない場合、および NULL が関数が処理する有効な期待値である場合は、const ポインターで渡します。
値をコピーするのにコストがかかる場合、および関数が指す値を変更したい場合、および NULL が関数が処理する有効な期待値である場合に、非 const ポインターで渡します。
値のコピーにコストがかかる場合、および関数が参照される値を変更したくない場合、const 参照によって渡します。また、ポインターが代わりに使用された場合、NULL は有効な値ではありません。
値のコピーにコストがかかる場合、および関数が参照される値を変更する必要がある場合、非 cont 参照で渡します。また、ポインタが代わりに使用された場合、NULL は有効な値ではありません。
テンプレート関数を記述する場合、考慮すべきいくつかのトレードオフがあり、この議論の範囲を超えているため、明確な答えはありませんが、ほとんどのテンプレート関数は値または (const) 参照によってパラメーターを取得すると言えば十分です。ただし、反復子の構文はポインターの構文と似ているため (「逆参照」のアスタリスク)、引数として反復子を期待するテンプレート関数は、デフォルトでポインターも受け入れます (NULL イテレーターの概念は構文が異なるため、NULL をチェックしません)。 )。
ここからわかることは、ポインターまたは参照パラメーターの使用を選択する場合の主な違いは、NULL が許容値であるかどうかということです。それでおしまい。
値が入力、出力、変更可能などであるかどうかは、結局のところ、関数に関するドキュメント/コメントに記載する必要があります。
Allen Holub の「自分の足を撃つには十分なロープ」には、次の 2 つのルールがリストされています。
120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers
彼は、参照が C++ に追加された理由をいくつか挙げています。
const
参照により、コピーを回避しながら値渡しセマンティクスを使用できます彼の主なポイントは、参照を「出力」パラメーターとして使用すべきではないということです。呼び出しサイトでは、パラメーターが参照か値パラメーターかを示すものがないためです。したがって、彼のルールはconst
参照のみを引数として使用することです。
個人的には、パラメーターが出力パラメーターであるかどうかがより明確になるため、これは良い経験則だと思います。ただし、私は個人的にこれに一般的に同意しますが、出力パラメーターを参照として主張する場合、チームの他の人の意見に左右されることを許可します (一部の開発者はそれらを非常に気に入っています)。
前の投稿の説明:
参照は、null 以外のポインターを取得する保証ではありません。(ただし、私たちはしばしばそれらをそのように扱います。)
恐ろしく悪いコードですが、森の悪いコードの背後にいるように、以下はコンパイルして実行されます: (少なくとも私のコンパイラの下では。)
bool test( int & a)
{
return (&a) == (int *) NULL;
}
int
main()
{
int * i = (int *)NULL;
cout << ( test(*i) ) << endl;
};
参照に関して私が抱えている本当の問題は、コンストラクターで割り当て、デストラクタで割り当てを解除し、コピー コンストラクターまたは operator=() の提供に失敗する、以降IDIOTSと呼ばれる他のプログラマーにあります。
突然、 foo(BAR bar)とfoo(BAR & bar)の間に違いの世界があります。(自動ビット単位のコピー操作が呼び出されます。デストラクタでの割り当て解除が 2 回呼び出されます。)
ありがたいことに、最新のコンパイラは、同じポインターのこの二重割り当て解除を検出します。15 年前はそうではありませんでした。(gcc/g++ では、setenv MALLOC_CHECK_ 0を使用して古い方法に戻ります。) DEC UNIX では、同じメモリが 2 つの異なるオブジェクトに割り当てられます。そこにはたくさんのデバッグの楽しみがあります...
より実用的に:
あまり。内部的には、参照渡しは、本質的に参照先オブジェクトのアドレスを渡すことによって実行されます。したがって、ポインターを渡すことによって得られる効率の向上は実際にはありません。
ただし、参照渡しには 1 つの利点があります。渡されるオブジェクト/タイプのインスタンスが保証されます。ポインターを渡すと、NULL ポインターを受け取るリスクがあります。参照渡しを使用することで、暗黙的な NULL チェックを関数の呼び出し元に 1 レベル上げます。