web-dev-qa-db-ja.com

ポインターと参照パラメーターの違いは?

これらは同じですか:

_int foo(bar* p) {
  return p->someInt();
}
_

そして

_int foo(bar& r) {
  return r.someInt();
}
_

NULLポインターの可能性を無視します。これらの2つの関数は、someInt()が仮想であるか、barまたはbarのサブクラスが渡されるかに関係なく、機能的に同一ですか?

このスライスは何ですか:

_bar& ref = *ptr_to_bar;
_
64
criddell

C++参照は、ポインターを使用して実装される標準では意図的に指定されていません。参照は、変数へのポインタというよりも、変数への「同義語」に似ています。このセマンティクスは、ポインターが状況によっては過剰であることに気付くことができる場合に、コンパイラーに可能な最適化を開きます。

さらにいくつかの違い:

  • 参照にNULLを割り当てることはできません。これは重大な違いであり、どちらかを好む主な理由です。
  • ポインターのアドレスを取得すると、ポインター変数のアドレスを取得します。参照のアドレスを取得すると、参照されている変数のアドレスを取得します。
  • 参照を再割り当てすることはできません。一度初期化されると、それはその寿命全体にわたって同じオブジェクトを指します。
65
shoosh

他の質問ではなく、他の質問では説明されていないポインタと参照の違いと、一方ではでき、もう一方ではできないすべての構文糖と可能性を無視します...それらは機能的にまったく同じです!どちらも関数を呼び出し、両方とも仮想関数を同様に適切に処理します。

いいえ、ラインはスライスされません。これは、ポインターによって指し示されたオブジェクトに参照を直接バインドしているだけです。

なぜ他のものを使用したいのかに関するいくつかの質問:

自分で違いを見つけようとする代わりに、知りたい場合はそれらに委任します。

参照は定数ポインターです。つまり、参照を変更して他のオブジェクトを参照することはできません。変更すると、参照オブジェクトの値が変更されます。

例:

       int j = 10;
       int &i = j;
       int l = 20;
       i = l; // Now value of j = 20

       int *k = &j;
       k = &l;   // Value of j is still 10
12
Vinay

はい、機能的には同じです。参照では、使用する前にオブジェクトに設定する必要があるため、無効なメモリへのNULLポインタまたはポインタを処理する必要はありません。

意味の違いを確認することも重要です。

  • 実際にオブジェクトを正常に渡す場合は参照を使用しますが、非常に大きいため、コピーを作成するのではなく、オブジェクトに参照を渡す方が理にかなっています(オブジェクトを変更していない場合)。
  • オブジェクトではなくメモリアドレスを処理する場合は、ポインターを使用します。
6

私は長い間C++を使っていませんので、あなたの質問に本当に答えようとはしません(ごめんなさい)。しかし、Eric Lippertが excellent article を投稿しました。私はあなたが指し示すと思われるポインタ/参照についてです。

5
Chris Shaffer

スライスについて一番下に隠されているあなたの2番目の質問に誰かが答えたかどうかはわかりません...それはスライスを引き起こしません。

スライスとは、派生オブジェクトが基本クラスオブジェクトに割り当てられた(コピーされた)場合です。派生クラスの特殊化は「スライス」されます。 objectがコピーされると言ったことに注意してください。コピー/割り当てられるポインタについてではなく、オブジェクト自体についてです。

あなたの例では、それは起きていません。参照の初期化で右辺値として使用されているBarオブジェクトへのポインターの参照を解除するだけです(その結果、Barオブジェクトになります)。用語が正しいかどうかわからない...

4
Dan

他の誰もが言及したように、実装では参照とポインタはほぼ同じです。いくつかの小さな警告があります:

  • 参照にNULLを割り当てることはできません(shooshがこれを言及しました)。「未定義」または「無効な」参照値がないため、これは重要です。

  • 一時変数をconst参照として渡すことができますが、一時変数にポインターを渡すことはできません。

たとえば、これは大丈夫です:

class Thingy; // assume a constructor Thingy(int,int)
void foo(const Thingy &a)
{ 
   a.DoSomething();
}

void bar( ) 
{
  foo( Thingy(1,2) );
}

しかし、ほとんどのコンパイラは文句を言うでしょう

void foo2( Thingy * a);

void bar2()
{
  foo( &Thingy(1,2) );
}
  • 変数のアドレスを取得してポインターを取得すると、コンパイラーは変数をメモリに保存します。ローカル変数への参照を割り当てると、同義語が作成されます。場合によっては、これによりコンパイラーがレジスターにデータを保持し、 load-hit-store を避けることができます。ただし、これはローカル変数にのみ適用されます。参照によって何かがパラメーターとして渡されると、それをスタックに保存することは避けられません。

void foo()
{
   int a = 5;
   // this may be slightly more efficient
   int &b = a;
   printf( "%d", ++b );
   // than this
   int *c = &a;
   printf( "%d", ++(*c) );
}
  • 同様に、 __ restrictキーワード は参照には適用できず、ポインターのみに適用できます。

  • 参照を使用してポインター演算を行うことはできません。そのため、配列へのポインターがある場合、配列の次の要素はp + 1を介して取得できます。

3
Crashworks

機能は明らかに「同じ」ではありませんが、仮想的な動作に関しては同様に動作します。スライスに関しては、これは参照またはポインターではなく、値を扱う場合にのみ発生します。

1
anon