C++/C++11での参照の使用

7140 ワード

リファレンス(reference)は複合タイプ(compound type)です.参照はオブジェクトに別の名前を付け、参照タイプは別のタイプを参照します.宣言子を&dと書くことで参照タイプを定義し、dは宣言された変数名である.
一、一般引用:
通常、変数を初期化すると、初期値が新しいオブジェクトにコピーされます.しかし、参照を定義するとき、プログラムは、初期値を参照にコピーするのではなく、参照とその初期値をバインドします.初期化が完了すると、リファレンスはその初期値オブジェクトと常にバインドされます.リファレンスの書き換えを別のオブジェクトにバインドできないため、リファレンスを初期化する必要があります.
参照に値を割り当てるのは、実際には参照にバインドされたオブジェクトに値を割り当てます.参照の値を取得します.実際には、参照にバインドされたオブジェクトの値を取得します.同様に、参照を初期値とし、実際には参照にバインドされたオブジェクトを初期値とします.
リファレンスはオブジェクトではありません.逆に、既存のオブジェクトに付けられた別の名前、リファレンス、別名にすぎません.参照を定義した後、その参照に対して行われるすべての操作は、バインドされたオブジェクト上で行われます.
参照自体はオブジェクトではないため、参照の参照を定義することはできません.
1つの文で複数の参照を定義できます.各参照識別子はシンボル&先頭でなければなりません.
リファレンスはオブジェクトにのみバインドされ、フォント値や式の計算結果にバインドされません.
二、constの引用:
リファレンスをconstオブジェクトにバインドすることができます.他のオブジェクトにバインドするように、定数へのリファレンス(reference to const)と呼ばれます.通常の参照とは異なり、定数の参照は、バインドされたオブジェクトを変更するために使用できません.
定数の参照は、参照が関与する操作のみを限定し、参照されるオブジェクト自体が定数であるかどうかは限定されません.オブジェクトも非常に量である可能性があるため、他の方法で値を変更できます.
三、引用パラメータを伝達する:
参照パラメータを使用すると、関数は1つ以上の実パラメータの値を変更できます.
リファレンスを使用してコピーを回避:大きなクラスタイプのオブジェクトをコピーするか、コンテナオブジェクトをコピーするのは効果的ではありません.IOタイプを含むクラスタイプでも、コピー操作はサポートされていません.コピー操作がサポートされていないタイプの場合、関数は参照パラメータを使用してのみそのタイプのオブジェクトにアクセスできます.
参照パラメータを使用して追加情報を返します.1つの関数では1つの値しか返されませんが、関数が複数の値を同時に返す必要がある場合があります.参照パラメータは、複数の結果を一度に返すのに有効な方法を提供します.
関数パラメータが参照を伝達する必要があるかどうかの判別:(1)、関数内部で実パラメータの値を変更する必要がある;(2)、関数の入力値が大きいので、コピーの代価を避けるために参照を使用します.
関数呼び出し時にメモリにコピーは生成されず、参照伝達関数のパラメータを用いることで、パラメータ伝達中にコピーが生成されないことを保証し、伝達の効率を高め、constの使用により、参照伝達の安全性を保証することができる.
Advantages of passing by reference:
(1)、It allows a function to change the value of the argument, which is sometimes useful.Otherwise, const references can be used to guarantee the function won’t change the argument.
(2)、Because a copy of the argument is not made, it is fast, even when used with large structs or classes.
(3)、References can be used to return multiple values from a function.
(4)、References must be initialized, so there’s no worry about null values.
Disadvantages of passing by reference:
(1)、Because a non-const reference cannot be made to an rvalue (e.g. a literal or an expression), reference arguments must be normal variables.
(2)、It can be hard to tell whether a parameter passed by non-const reference is meant to be input, output, or both. Judicious use of const and a naming suffix for out variables can help.
(3)、It’s impossible to tell from the function call whether the argument may change. An argument passed by value and passed by reference looks the same. We can only tell whether an argument is passed by value or reference by looking at the function declaration. This can lead to situations where the programmer does not realize a function will change the value of the argument.
When to use pass by reference:
(1)、When passing structs or classes (use const if read-only).
(2)、When you need the function to modify an argument.
When not to use pass by reference:
(1)、When passing fundamental types (use pass by value).
四、右の値の参照:
移動操作をサポートするために、C++11は新しい参照タイプである右値参照(rvalue reference)を導入した.右値参照とは、右値にバインドしなければならない参照です.右の値の参照は、&ではなく&&で取得されます.右の値の参照には、破棄するオブジェクトにのみバインドできる重要な性質があります.したがって、右の値で参照されるアセット(Asset)を別のオブジェクトに自由に移動できます.
一般的に、左の式はオブジェクトのアイデンティティを表し、右の式はオブジェクトの値を表します.
任意の参照と同様に、右の値の参照もオブジェクトの別の名前にすぎません.通常のリファレンス(右のリファレンスと区別するために左のリファレンス(lvalue reference)と呼ぶことができます)については、変換を要求する式、字面定数、または右の値を返す式にバインドすることはできません.右値参照にはまったく逆のバインドプロパティがあります.このような式に右値参照をバインドできますが、左値に直接バインドすることはできません.
左値参照を返す関数は、付与、下付き、逆参照、および前のインクリメント/減算演算子とともに、左値式を返す例です.このような式の結果に左の参照をバインドできます.
非参照タイプの関数を返し、算術、関係、ビット、および後置増分/減算演算子とともに右値を生成します.このような式には、左の参照をバインドすることはできませんが、constの左の参照または右の参照をバインドすることができます.
左の値は永続的で、右の値は短い:左の値は永続的な状態があり、右の値は字面定数か、式の評価中に作成された一時オブジェクトです.
右の値の参照は一時オブジェクトにのみバインドされるため、参照されたオブジェクトは破棄されます.このオブジェクトには他のユーザーはいません.この2つの特性は、右の値で参照されたコードを使用して、参照されたオブジェクトのリソースを自由に管理できることを意味します.
変数は左です.変数は演算子なしで演算オブジェクトが1つしかない式と見なすことができます.
変数は左の値なので、右の値参照を直接変数にバインドすることはできません.この変数が右の値参照タイプであってもできません.
右の値の参照を左の値に直接バインドすることはできませんが、左の値を対応する右の値の参照タイプに表示的に変換できます.また、moveという新しい標準ライブラリ関数を呼び出すことで、ヘッダファイルutilityに定義された左値にバインドされた右値参照を得ることもできます.move呼び出しは、左の値がありますが、右の値のように処理したいことをコンパイラに伝えます.moveを呼び出した後、移動後のソースオブジェクトの値を仮定することはできません.
移動元オブジェクトを破棄したり、新しい値を付与したりできますが、移動元オブジェクトの値は使用できません.
C++11の右値参照の詳細については、次のパラメータを参照してください.http://blog.csdn.net/fengbingchun/article/details/52562004 
五、参照を関数として返す値
参照で関数値を返します.関数を定義するときは、関数名に&を付ける必要があります.参照で関数を返す最大の利点は、メモリに返される値のコピーが生成されないことです.
参照は戻り値として、次のルールに従う必要があります.
(1)、ローカル変数の参照を返すことはできません.
(2)、関数内部newで割り当てられたメモリの参照を返すことができません.
(3)、クラスメンバーの参照を返すことができますが、constが望ましいです.
関数の戻り値タイプが参照の場合、一般的には参照タイプで受信されますが、非参照タイプで受信すると、関数が返す参照のデータ値が受信オブジェクトにコピーされ、関数が非参照タイプを返すのと同じ効果があります.
常参照を関数の戻り値として返すことはできません.
常引用できるところはできるだけ常引用を使う.
テストコードは次のとおりです.
#include "reference.hpp"
#include 
#include  // std::move

int test_reference_1()
{
	// 1.      
	int ival = 1024;
	int &refVal = ival; // refVal  ival( ival      )
	//int &refVal2; // error:        
	int &refVal3 = refVal; // refVal3       refVal      ,       ival 
	//    refVal            i
	int i = refVal; // correct:i     ival  
	//               ,               &  
	int i1 = 1024, i2 = 2048;
	int &r = i1, r2 = i2;
	int i3 = 1024, &ri = i3;
	int &r3 = i3, &r4 = i2; // r3 r4    
	//int &refVal4 = 10; // error:                
	double dval = 3.14;
	//int &refVal5 = dval; // error:              int   
	const int &refVal6 = dval; // correct
	//                :
	//const int temp = dval; //                   
	//const int &refVal6 = temp; //  refVal6       

	// 2. const   
	//                     
	const int ci = 1024;
	const int &r1 = ci; // correct:              
	//r1 = 42; // error: r1       
	//int &r2 = ci; // error:                   

	int i4 = 42;
	const int &r6 = i4; //    const int&       int   
	const int &r7 = 42; // correct: r7       
	const int &r8 = r6 * 2; // correct: r8       
	//int &r9 = r6 * 2; // error: r9           

	//                     ,                    
	int x = 42;
	int &rx = x; //   rx    x
	const int &ry = x; // ry     x,       ry  x  
	rx = 0; // rx    ,x     0
	//ry = 0; // error: ry       

	return 0;
}

//        int     ,         0
static void reset(int &i) // i   reset           
{
	i = 0; //    i       
}

int test_reference_2()
{
	int j = 42;
	reset(j); // j      ,      
	fprintf(stderr, "j = %d
", j); // j = 0 return 0; } int test_reference_3() { int i = 42; int &r = i; // correct: r i //int &&rr = i; // error: //int &r2 = i * 42; // error: i*42 const int &r3 = i * 42; // correct: const int &&rr2 = i * 42; // correct: rr2 int &&rr1 = 42; // correct: //int &&rr3 = rr1; // error: rr1 int &&rr4 = std::move(rr1); // correct return 0; }

GitHub: https://github.com/fengbingchun/Messy_Test