54

関数への参照を渡すときにポインターを逆参照するとどうなりますか?

これが簡単な例です

int& returnSame( int &example ) { return example; }

int main()
{
  int inum = 3;
  int *pinum = & inum;

  std::cout << "inum: " <<  returnSame(*pinum) << std::endl;

  return 0;          

}

一時オブジェクトが生成されていますか?

4

3 に答える 3

50

ポインタを逆参照してもコピーは作成されません。ポインタのターゲットを参照する左辺値を作成します。これは左辺値参照引数にバインドできるため、関数はポインターが指すオブジェクトへの参照を受け取り、同じオブジェクトへの参照を返します。この動作は明確に定義されており、一時的なオブジェクトは含まれていません。

引数を値で取得すると、ローカルコピーが作成され、それへの参照を返すことは不適切であり、アクセスされた場合に未定義の動作が発生します。

于 2012-07-05T16:29:24.053 に答える
33

書かれたあなたの質問への答え

いいえ、この動作は定義されています。new演算子が型のデフォルトコンストラクターを呼び出す次のスニペットのように、プログラマーによって明示的に指定されない限り、ポインター型が逆参照されるか参照型が使用される場合、コンストラクターは呼び出されませんint

int* variable = new int;

書かれているように、実際に起こっていることに関してreturnSame(*pinum)は、と同じ変数ですinum。これを自分で確認したい場合は、次のスニペットを使用できます。

returnSame(*pinum) = 10;
std::cout << "inum: " << inum << std::endl;

さらなる分析

提供されたコードを修正することから始めます。これは、投稿する前にコンパイルしようとしたようには見えません。編集後、残りの1つのエラーは最初の行にあります。

int& returnSame( int &example ) { return example; } // semi instead of colon

ポインタとリファレンス

ポインターと参照はコンパイラーによって同じように扱われますが、それらの使用法は異なり、実装はそれほど異なりません。ポインタ型と参照型は、それらの値として、他の何かの場所を格納します。ポインターの逆参照(*または->演算子を使用)は、値自体ではなく、ポインターをたどり、ポインターが参照する場所で操作を実行するコードを生成するようにコンパイラーに指示します。ポインターを逆参照する場合、新しいデータは割り当てられません(コンストラクターは呼び出されません)。

参照の使用はほとんど同じように機能しますが、コンパイラーは、場所自体ではなく場所に値が必要であると自動的に想定する点が異なります。実際のところ、ポインタで指定された場所を参照するのと同じ方法で参照することはできません。一度割り当てられると、参照を再配置(変更)することはできません(つまり、未定義の動作に依存することはできません)。&演算子を使用することで、その値を取得できます。NULL参照を持つことも可能ですが、これらの処理は特に注意が必要であり、使用することはお勧めしません。

スニペット分析

int *pinum = & inum;

既存の変数inumを指すポインターを作成します。ポインタの値は、inumが格納されているメモリアドレスです。ポインタを作成して使用しても、ポイントされたオブジェクトのコンストラクタは暗黙的に呼び出されません。このタスクはプログラマーに任されています。

*pinum

ポインターを間接参照すると、通常の変数が効果的に生成されます。この変数は、概念的には、別の名前付き変数が使用するのと同じスペースを占める場合と、そうでない場合があります。この場合、*pinuminumは同じ変数です。「プロデュース」と言うときは、コンストラクターが呼び出されないことに注意することが重要です。これが、ポインタを使用する前にポインタを初期化する必要がある理由です。ポインタの逆参照によってストレージが割り当てられることはありません。

returnSame(*pinum)

この関数は参照を受け取り、同じ参照を返します。この関数はポインターを使用して作成することもでき、まったく同じように動作することを理解しておくと役立ちます。参照は、コンストラクターを呼び出さないという点で、初期化も実行しません。ただし、初期化されていない参照を持つことは違法であるため、それらを介して初期化されていないメモリにぶつかることは、ポインタの場合ほど一般的な間違いではありません。関数は、次の方法でポインターを使用するように書き直すことができます。

int* returnSamePointer( int *example ) { return example; }

この場合、ポインタを渡す前にポインタを逆参照する必要はありませんが、出力する前に関数の戻り値を逆参照する必要があります。

std::cout << "inum: " <<  *(returnSamePointer(pinum)) << std::endl;

NULL参照

NULL参照を宣言すると危険です。これを使用しようとすると、自動的に逆参照が試行され、セグメンテーション違反が発生するためです。ただし、参照がnull参照であるかどうかを安全に確認できます。繰り返しになりますが、これらを使用しないことを強くお勧めします。

int& nullRef = *((int *) NULL);      // creates a reference to nothing
bool isRefNull = (&nullRef == NULL); // true

概要

  • ポインタ型と参照型は、同じことを実現するための2つの異なる方法です。
  • 一方に適用される落とし穴のほとんどは、もう一方にも適用されます
  • ポインタも参照も、どのような状況でも暗黙的に参照値のコンストラクタまたはデストラクタを呼び出すことはありません。
  • ポインターが適切に初期化されている限り、間接参照ポインターへの参照を宣言することは完全に合法です。
于 2012-07-05T16:06:24.040 に答える
2

コンパイラは何も「呼び出し」ません。コードを生成するだけです。ポインタの間接参照は、最も基本的なレベルでは、ある種のロード命令に対応しますが、現在のコードでは、コンパイラはこれを簡単に最適化して、値を直接出力するか、ロードに直接ショートカットすることができますinum

「一時オブジェクト」について:ポインタを間接参照すると、常に左辺値が得られます。

おそらく、あなたの質問にはもっと興味深い質問が隠されています:コンパイラは関数の引数を参照として渡すことをどのように実装しますか?

于 2012-07-05T15:06:44.973 に答える