回答:
ポインターは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);
func2 passes by reference
。コードレベルの観点でポインターを渡すことによって実装された、高レベルの観点から「参照によって」渡すことを意味していたと思いますが、これは非常に混乱しました(stackoverflow.com/questions/13382356/…を参照)。
func(int& a)
標準のどのバージョンでも有効なCではありません。誤ってファイルをC ++としてコンパイルしている可能性があります。
nothing
。これは、オプションの引数を提供するために使用できます。string s = &str1 + &str2;
、ポインタを使用して行うことはできません 。void f(const T& t); ... f(T(a, b, c));
のアドレスを取得できないため、ポインターをそのように使用することはできません。「cplusplus.com」の記事による推論が好きです。
関数がパラメーターを変更する必要がなく、値をコピーするのが簡単な場合(int、double、char、boolなど...単純型。std:: string、std :: vector、およびその他のすべてのSTLの場合、値渡しします。コンテナーは単純なタイプではありません。)
値のコピーに負荷がかかり、かつ関数がAND NULLを指す値を変更したくない場合は、constポインターを渡します。これは、関数が処理する有効な期待値です。
値のコピーにコストがかかり、関数がAND NULLが指す値を変更する必要がある場合、非constポインターを渡します。これは、関数が処理する有効な期待値です。
値をコピーするのに負荷がかかり、関数が参照された値を変更したくない場合にconst参照で渡すAND AND NULLは、代わりにポインターが使用された場合、有効な値ではありません。
値のコピーにコストがかかり、かつ関数が参照する値を変更する必要がある場合に非cont参照で渡し、AND NULLが参照されている値を変更したい場合、代わりにポインターが使用された場合は有効な値ではありません。
テンプレート関数を記述する場合、明確な答えはありません。考慮すべきトレードオフがいくつかあるため、この説明の範囲を超えていますが、ほとんどのテンプレート関数は値または(const)参照によってパラメーターを取ると言えば十分ですただし、イテレータの構文はポインタの構文と似ているため(アスタリスクは「逆参照」)、イテレータを引数として期待するテンプレート関数は、デフォルトでポインタも受け入れます(NULLイテレータの概念は構文が異なるため、NULLをチェックしません) )。
これから私が理解することは、ポインターまたは参照パラメーターを使用することを選択することの間の主な違いは、NULLが許容可能な値であるかどうかです。それでおしまい。
結局のところ、値が入力、出力、変更可能などであるかどうかは、関数に関するドキュメント/コメントに記載する必要があります。
アレン・ホルブの「足で自分を撃つのに十分なロープ」には、次の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つの異なるオブジェクトに割り当てられます。そこにはたくさんのデバッグの楽しみがあります...
より実際的には:
*i
、プログラムは未定義の動作をします。たとえば、コンパイラはこのコードを見て、「OK、このコードはすべてのコードパスで未定義の動作をしているため、この関数全体が到達不能でなければならない」と想定できます。次に、この関数につながるすべての分岐が行われないと仮定します。これは定期的に実行される最適化です。
ここでの回答のほとんどは、意図を表現するという点で、関数シグネチャに生のポインタを持っていることの固有のあいまいさには対処できません。問題は次のとおりです。
呼び出し元は、ポインタが単一のオブジェクトを指しているのか、それともオブジェクトの「配列」の先頭を指しているのかわかりません。
呼び出し元は、ポインタが指すメモリを「所有」しているかどうかはわかりません。IE、関数がメモリを解放する必要があるかどうか。(foo(new int)
-これはメモリリークですか?)。
呼び出し元はnullptr
、関数に安全に渡すことができるかどうかを知りません。
これらの問題はすべて参照によって解決されます。
参照は常に単一のオブジェクトを参照します。
参照は、参照するメモリを所有することはなく、単にメモリへのビューです。
参照をnullにすることはできません。
これにより、参照は一般的な使用に適した候補になります。ただし、参照は完全ではありません。考慮すべき主要な問題がいくつかあります。
&
、ポインタを渡していることを示す演算子を。たとえばint a = 5; foo(a);
、aが参照渡しされており、変更できることは、ここではまったく明確ではありません。std::optional<T&>
(正当な理由のために)有効ではありません、ポインタは私たちにあなたがしたいことNULL値の許容を与えます。それで、明示的な間接指定を伴うnull可能な参照が必要な場合は、T*
権利に到達する必要があるようです。違う!
ヌル可能性を求める必死の中で、私たちはに手を伸ばしT*
、前述の欠点と意味のあいまいさをすべて無視することができます。代わりに、C ++が最も得意とすること、つまり抽象化に到達する必要があります。ポインターをラップするクラスを記述するだけで、表現可能性が得られるだけでなく、null可能性と明示的な間接参照も得られます。
template <typename T>
struct optional_ref {
optional_ref() : ptr(nullptr) {}
optional_ref(T* t) : ptr(t) {}
optional_ref(std::nullptr_t) : ptr(nullptr) {}
T& get() const {
return *ptr;
}
explicit operator bool() const {
return bool(ptr);
}
private:
T* ptr;
};
これは私が思いつくことができる最もシンプルなインターフェースですが、それは効果的に機能します。参照の初期化、値が存在するかどうかの確認、および値へのアクセスを可能にします。次のように使用できます。
void foo(optional_ref<int> x) {
if (x) {
auto y = x.get();
// use y here
}
}
int x = 5;
foo(&x); // explicit indirection here
foo(nullptr); // nullability
私たちは目標を達成しました!生のポインタと比較して、利点を見てみましょう。
nullptr
関数の作成者が明示的に要求しているので、呼び出し元は渡されることができますoptional_ref
ここから、等価演算子、モナディックget_or
およびmap
インターフェース、値を取得するメソッド、または例外をスローするメソッドを追加するなど、インターフェースをより複雑にすることができますconstexpr
。それはあなたがすることができます。
結論として、生のポインタを使用する代わりに、それらのポインタが実際にコードで何を意味するのかを推論し、標準ライブラリの抽象化を利用するか、独自に作成します。これにより、コードが大幅に改善されます。
あんまり。内部的には、参照による受け渡しは、参照されるオブジェクトのアドレスを本質的に渡すことによって実行されます。したがって、ポインタを渡すことによる効率の向上はありません。
ただし、参照渡しは1つの利点があります。渡されるオブジェクト/タイプのインスタンスがあることが保証されています。ポインタを渡すと、NULLポインタを受け取る危険があります。参照渡しを使用すると、関数の呼び出し元に暗黙のNULLチェックを1レベル押し上げます。
new
ポインタとその結果生じる所有権の問題を作成することを忘れないでください。