web-dev-qa-db-ja.com

参照渡し時のポインターの逆参照

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

これは簡単な例です

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

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

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

  return 0;          

}

作成された一時オブジェクトはありますか?

40
MWright

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

値で引数を受け取った場合、ローカルコピーが作成され、そのコピーへの参照が返され、アクセスされた場合の動作が未定義になります。

38
Mike Seymour

書面による質問への回答

いいえ、この動作は定義されています。 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
_

ポインターと参照

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

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

スニペット分析

_int *pinum = & inum;
_

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

_*pinum
_

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

_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つの異なる方法です
  • 一方に当てはまる落とし穴のほとんどは、もう一方にも当てはまります
  • ポインターも参照も、どのような状況でも暗黙的に参照値のコンストラクターまたはデストラクターを呼び出すことはありません
  • ポインターが適切に初期化されている限り、逆参照されたポインターへの参照を宣言することは完全に合法です。
27
Wug

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

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

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

1
Kerrek SB