C++の参照の詳細

120419 ワード

C++の参照は、ある変数(ターゲット)の別名であり、参照の操作は変数の直接操作と全く同じです.参照の宣言方法:タイプ識別子&参照名=ターゲット変数名;
【例1】:

  
  
  
  
  1. int a; int &ra=a; // ra, a ,  

説明:
(1)&ここではアドレス演算ではなく,識別の役割を果たす.
(2)タイプ識別子とは、ターゲット変数のタイプを指す.
(3)参照を宣言する場合は、同時に初期化する必要があります.
(4)参照宣言が完了すると,ターゲット変数名に相当する2つの名前,すなわちそのターゲット元名と参照名があり,その参照名を他の変数名の別名として使用することはできない.

  
  
  
  
  1. ra=1;   
  2.    
  3. a=1; 

(5)参照を宣言し、新たに定義された変数ではなく、その参照名がターゲット変数名の別名であることを示し、それ自体がデータ型ではないため、参照自体が記憶ユニットを占めず、システムも参照に記憶ユニットを割り当てない.したがって、参照に対してアドレスを求めるのは、ターゲット変数に対してアドレスを求めることです.&raは&aと等しい.
(6)配列の参照を確立できない.配列はいくつかの要素からなる集合であるため、配列の別名を作成することはできません.
リファレンスの適用
1、パラメータとして参照
参照の重要な役割の1つは、関数としてのパラメータです.従来のC言語では関数パラメータ伝達は値伝達であり,大きなデータがパラメータ伝達として用いられていた場合,ブロック全体をスタック化することを避けることができ,プログラムの効率を向上させることができるため,ポインタを用いることが多かった.しかし、現在(C++では)同じ効率的な選択(特定の場合に必要な選択)が追加されています.参照です.
【例2】:

  
  
  
  
  1. void swap(int &p1, int &p2) // p1, p2    
  2. int p; p=p1; p1=p2; p2=p; } 

この関数をプログラムで呼び出すには、対応するプライマリ・コール関数の呼び出しポイントで、パラメータ変数に特別な要求がなくても、変数を実パラメータとして直接呼び出せばよい.たとえば、上記で定義したswap関数に対応して、対応するプライマリ・コール関数は次のように書くことができます.

  
  
  
  
  1. main( )  
  2. {   
  3.  int a,b;  
  4.  cin>>a>>b; // a,b  
  5.  swap(a,b); // a b swap    
  6.  cout<<a<< ' ' <<b; //    

上記プログラムの実行時に、データ10 20を入力して車に戻った場合、出力結果は20 10となる.
【例2】から分かるように:
(1)伝達参照が関数に伝達されるのと伝達ポインタの効果は同じである.この場合、被変調関数のパラメータは、元の主変調関数の実パラメータ変数またはオブジェクトの別名として使用されるので、被変調関数でのパラメータ変数に対する操作は、対応するターゲットオブジェクト(主変調関数)に対する操作である.
(2)参照伝達関数のパラメータを使用して、メモリに実パラメータのコピーが生成されず、実パラメータに対して直接操作される.一般変数伝達関数のパラメータを使用して、関数呼び出しが発生した場合、実パラメータ変数のコピーであるパラメータに記憶ユニットを割り当てる必要があります.オブジェクトが渡された場合、コピーコンストラクション関数も呼び出されます.したがって,パラメータで伝達されるデータが大きい場合,参照は一般変数で伝達されるパラメータよりも効率的で占有空間的である.
(3)ポインタを関数のパラメータとして用いることで参照を用いることとの効果も得られるが,被変調関数でも同様にパラメータにメモリセルを割り当てる必要があり,「*ポインタ変数名」の形式で演算を繰り返す必要があり,エラーが発生しやすくプログラムの読解性が劣る.一方,主調関数の呼び出し点では,変数のアドレスを実パラメータとしなければならない.参照は使いやすく、はっきりしています.
リファレンスを使用してプログラムの効率を向上させ、関数に渡されるデータが関数で変更されないように保護する場合は、通常のリファレンスを使用します.
2、常引用
常引用宣言方式:constタイプ識別子&参照名=ターゲット変数名;
このように宣言された参照は、参照によってターゲット変数の値を変更することはできず、参照のターゲットをconstとし、参照の安全性を達成します.
【例3】:

  
  
  
  
  1. int a ;  
  2. const int &ra=a;  
  3. ra=1; //  
  4. a=1; //  

これは、コードをより丈夫にするだけでなく、他の必要性もあります.
【例4】:次の関数宣言があると仮定する.

  
  
  
  
  1. string foo( );  
  2. void bar(string & s); 

次の式は不正です.

  
  
  
  
  1. bar(foo( ));  
  2. bar("hello world"); 

なぜならfoo()と「hello world」列には一時オブジェクトが生成され、C++ではconstタイプの一時オブジェクトが生成されるからです.したがって、上記の式は、constタイプのオブジェクトを非constタイプに変換しようとするのは違法です.
参照型パラメータはconstとして定義できる場合、できるだけconstとして定義する必要があります.
3、戻り値として参照
参照で関数の値を返すには、次の形式で関数を定義します.
タイプ識別子&関数名(パラメータリストおよびタイプ説明){関数体}
説明:
(1)戻り関数値を参照し、関数を定義する際に関数名に&
(2)参照で関数値を返す最大の利点は,メモリに返される値のコピーが生成されないことである.
【例5】以下のプログラムでは、一般的な関数fn 1(関数値を返す方法で返す)が定義され、他の関数fn 2は、参照された方法で関数値を返す.

  
  
  
  
  1. #include <iostream.h>  
  2. float temp; // temp  
  3. float fn1(float r); // fn1  
  4. float &fn2(float r); // fn2  
  5. float fn1(float r) // fn1,  
  6. {   
  7.  temp=(float)(r*r*3.14);   
  8.  return temp;   
  9. }  
  10. float &fn2(float r) // fn2,  
  11. {   
  12.  temp=(float)(r*r*3.14);   
  13.  return temp;  
  14. }  
  15. void main() //  
  16. {   
  17.  float a=fn1(10.0); // 1 , ( )  
  18.  float &b=fn1(10.0); // 2 , (  C++ )  
  19.  //  
  20.  float c=fn2(10.0); // 3 ,  
  21.  //  
  22.  float &d=fn2(10.0); // 4 ,  
  23.  //  
  24.  cout<<a<<c<<d;  

参照は戻り値として、次のルールに従う必要があります.
(1)ローカル変数の参照を返すことはできません.これは、Effective C+[1]のItem 31を参照することができる.主な理由は、ローカル変数が関数の戻り後に破棄されるため、返される参照が「指なし」の参照となり、プログラムが未知の状態になるためです.
(2)関数内部newで割り当てられたメモリの参照を返すことができない.これは、Effective C+[1]のItem 31を参照することができる.局所変数のパッシブ破棄の問題は存在しないが,この場合(関数内部のnew割り当てメモリの参照を返す)には,他の気まずい状況に直面する.例えば、関数によって返される参照は一時変数として現れるだけで、実際の変数が与えられていないと、この参照が指す空間(newによって割り当てられる)は解放されず、memory leakとなる.
(3)クラスメンバーの参照を返すことができますが、constが望ましいです.この原則は、Effective C+[1]のItem 30を参照することができる.主な理由は、オブジェクトのプロパティがビジネス・ルールに関連付けられている場合、その付与は他のプロパティまたはオブジェクトのステータスに関連することが多いため、付与操作をビジネス・ルールにカプセル化する必要があるからです.他のオブジェクトが属性の非常に多くの参照(またはポインタ)を取得できる場合、属性の単純な付与はビジネス・ルールの完全性を破壊します.
(4)参照と一部のオペレータのリロード:
ストリームオペレータ<<と>>は、cout<<「hello」<【例6】試験は、付与式の左の値として参照された関数値を返す.

  
  
  
  
  1. #include <iostream.h>  
  2. int &put(int n);  
  3. int vals[10];  
  4. int error=-1;  
  5. void main()  
  6. {  
  7. put(0)=10; // put(0) , vals[0]=10;   
  8. put(9)=20; // put(9) , vals[9]=10;   
  9. cout<<vals[0];   
  10. cout<<vals[9];  
  11. }   
  12. int &put(int n)  
  13. {  
  14. if (n>=0 && n<=9 ) return vals[n];   
  15. else { cout<<"subscript error"return error; }  

(5)他のオペレータでは、+-*/四則演算子を返すことはできません.それらは参照を返すことができず、Effective C++[1]のItem 23はこの問題を詳細に議論した.主な理由は、この4つのオペレータにside effectがないため、オブジェクトを戻り値として構築する必要があります.オプションとして、オブジェクトを返し、ローカル変数の参照を返し、newで割り当てられたオブジェクトの参照を返し、静的イメージ参照を返します.前述した参照を戻り値とする3つのルールに基づいて,2,3の2つのスキームはいずれも否決された.静的オブジェクトの参照は、((a+b)=(c+d))が常にtrueであるため、エラーが発生します.オプションは1つのオブジェクトを返すだけです.
4、参照とマルチステート
リファレンスは、ポインタ以外のマルチステート効果を生成する手段です.これは、ベースクラスの参照が派生クラスインスタンスを指すことができることを意味します.
【例7】:

  
  
  
  
  1. class  A;  
  2. class  B:public A{……};  
  3. B  b;  
  4. A  &Ref = b; //   

Refは、派生クラスオブジェクトのベースクラスから継承されたメンバーにのみアクセスできます.ベースクラス参照は派生クラスを指します.Aクラスに虚関数が定義され、Bクラスにこの虚関数が書き換えられていれば、Refによってマルチステート効果を生成することができる.
引用まとめ
(1)参照の使用において,ある変数に単純に別名を付けることは意味がなく,参照の目的は主に関数パラメータ伝達において,大きなブロックデータやオブジェクトの伝達効率や空間の望ましくない問題を解決するために用いられる.
(2)参照伝達関数のパラメータにより,パラメータ伝達中にコピーが発生しないことを保証し,伝達の効率を向上させ,constの使用により参照伝達の安全性を保証する.
(3)参照とポインタの違いは,ポインタがあるポインタ変数によって1つのオブジェクトを指した後,その指す変数に対して間接的に操作することである.プログラムにはポインタが使用され、プログラムの可読性が悪い.参照自体がターゲット変数の別名であり、参照に対する操作はターゲット変数に対する操作である.
(4)参照のタイミングを使う.ストリームオペレータ<<と>、割り当てオペレータ=の戻り値、コピーコンストラクタのパラメータ、割り当てオペレータ=のパラメータ、その他の場合は参照を推奨します.
以上の内容の绍介を通じて、あなたに助けをもたらすことができることを望みます.