[C++]参照(&)詳細

31168 ワード

転載先:https://www.cnblogs.com/duwenxing/p/7421100.html
一、引用とは
参照、名前の通り変数またはオブジェクトの別名であり、参照の操作はバインドされた変数またはオブジェクトの操作と完全に等価です.
  :   &   =     ;

特に注意:
1.参照記号を作る場合、&アドレス演算子に関係なくフラグとして機能する
2.参照のタイプは、バインドされた変数のタイプと同じである必要があります.
3.参照を宣言しながら初期化する必要があります.そうしないと、システムがエラーを報告します.
#include
using namespace std;
int main()
{
    int &a; //  !              
    return 0;
}

4.参照は変数またはオブジェクトの別名に相当するため、既存の参照名を他の変数またはオブジェクトの名前または別名として使用することはできません.
5.参照は新しい変数やオブジェクトを定義しないため、メモリは参照のために新しい空間を開いてこの参照を格納しません.
 #include
 using namespace std;
 int main(){
    int value=10;
    int &new_value=value;
    cout<<"value        :"<<&value<<endl;
    cout<<"new_value        :"<<&new_value<<endl;
    return 0;
 }

両方のアドレスが一致していることを確認できます.
6.配列への参照
  :   (&   )[       ]=   ;
#include
using namespace std;
int main(){
    int a[3]={1,2,3};
    int (&b)[3]=a;                 //       
    cout<<&a[0]<<" "<<&b[0]<<endl;
    cout<<&a[1]<<" "<<&b[1]<<endl;
    cout<<&a[2]<<" "<<&b[2]<<endl;
    return 0;
}
//    :
0x6ffe20     0x6ffe20  
0x6ffe24     0x6ffe24  
0x6ffe28     0x6ffe28  

7.ポインタへの参照
  :   *&   =   ;
//     :(  *) &   =   ,           *
#include
using namespace std;
int main(){
    int a=10;
    int *ptr=&a;
    int *&new_ptr=ptr;
    cout<<&ptr<<" "<<&new_ptr<<endl;
    return 0; 
}
//    :
0x6ffe38     0x6ffe38   

二、引用の応用
A.関数として参照するパラメータ
例:数値交換
#include
using namespace std;
void swap(int &a,int &b){//         
    int temp=a;
    a=b;
    b=temp; 
}
int main(){
    int value1=10,value2=20;
    cout<<"----------------------   ----------------------------"<<endl;
    cout<<"value1   :"<<value1<<endl; 
    cout<<"value2   :"<<value2<<endl;
    swap(value1,value2); 
    cout<<"----------------------   ----------------------------"<<endl;
    cout<<"value1   :"<<value1<<endl; 
    cout<<"value2   :"<<value2<<endl;
    return 0;
}

出力結果:
----------------------   ----------------------------
value1   :10
value2   :20
----------------------   ----------------------------
value1   :20
value2   :10

特に注意:1.上記の関数では、実際には、実パラメータ変数やオブジェクトの値をパラメータにコピーするのではなく、実パラメータ自体を操作します.2.ポインタを関数として使用するパラメータは、参照と同様の効果が得られますが、関数を呼び出すときにも、ポインタ変数のメモリにスペースを割り当てる必要があります.参照は必要ありません.したがって、ポインタではなくC++で参照を関数のパラメータとして使用することを推奨します.3.プログラミング中に、関数として参照されるパラメータによって関数のプログラミング効率を向上させ、伝達されるパラメータを関数で変更しないように保護したい場合は、定数への参照を関数のパラメータとして使用する必要があります.4.配列の参照を関数のパラメータとする:C++の配列タイプは長さ情報を持つものであり、参照伝達時に配列を示す場合は配列の長さを指定しなければならない.例:
#include
using namespace std;
void func(int(&a)[5])  //           ,         
{ 
  ... 
}

int main(){
    int number[5]={0,1,2,3,4};
    func(number); 
    return 0; 
 }

B.常引用
  :const    &   =     ;

通常のリファレンスでは、バインドされた変数またはオブジェクトの変更は許可されません.
#include
using namespace std;
int main(){
    int a=10;
    const int &new_a=a;
    new_a=11;//  !                        
    return 0;
}

特に注意:
まず次の例を見てみましょう.
#include
#include 
using namespace std;
string func1(){
    string temp="This is func1";
    return temp;
}
void func2(string &str){
    cout<<str<<endl;
}
int main(){
    func2(func1());
    func2("Tomwenxing");
    return 0;
}

上のプログラムコンパイラを実行するとエラーが表示されます.これはfunc 1()と「Tomwenxing」がシステムに一時オブジェクト(stringオブジェクト)を生成して格納し、C++のすべての一時オブジェクトがconstタイプであり、上記のプログラムがconstオブジェクト以外のオブジェクトに値を付与しようとすると、プログラムがエラーを報告するに違いないからです.関数func 2のパラメータの前にconstを追加すると、プログラムは正常に動作します.
#include
#include 
using namespace std;
string func1(){
    string temp="This is func1";
    return temp;
}
void func2(const string &str){
    cout<<str<<endl;
}
int main(){
    func2(func1());
    func2("Tomwenxing");
    return 0;
}

C.関数としての戻り値を参照
  :   &   (    ){     }

特に注意:
1.関数としての戻り値を参照する場合は、関数を定義するときに関数名に&を付ける必要があります.
2.関数として参照される戻り値の最大の利点は、メモリに戻り値のコピーが生成されないことです.
//    :RUNOOB
#include
using namespace std;
float temp;
float fn1(float r){
    temp=r*r*3.14;
    return temp;
} 
float &fn2(float r){ //&      temp   ,        temp  
    temp=r*r*3.14;
    return temp;
}
int main(){
    float a=fn1(5.0); //case 1:   
    //float &b=fn1(5.0); //case 2:                 [Error] invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
                           //(              ,      warning) 
    float c=fn2(5.0);//case 3:    
    float &d=fn2(5.0);//case 4:                  
    cout<<a<<endl;//78.5
    //cout<
    cout<<c<<endl;//78.5
    cout<<d<<endl;//78.5
    return 0;
}

3.ローカル変数の参照を返すことはできません.上記の例のように、tempがローカル変数である場合、関数が戻った後に破棄され、tempへの参照が「指なし」の参照となり、プログラムは未知の状態になります.
4.関数内でnewで割り当てられたメモリの参照を返すことはできません.ローカル変数のパッシブ破棄の問題はありませんが、返される関数の参照が一時変数としてのみ発生し、実際の変数に値を付与しないと、この参照が指す空間(new割り当てがある)が解放できない場合(具体的な変数名がないためdeleteでメモリを手動で解放できない場合)、メモリが漏洩します.そのため、このような状況の発生を避けるべきだ.
5クラスメンバーの参照を返す場合はconst参照が望ましい.これにより、クラスのメンバーが無意識に破壊されることを回避できます.
6.代入式の左の値として関数で返される参照
#include
using namespace std;
int value[10];
int error=-1;
int &func(int n){
    if(n>=0&&n<=9)
        return value[n];//                  ,              
    else
        return error;
}

int main(){
    func(0)=10;
    func(4)=12;
    cout<<value[0]<<endl;
    cout<<value[4]<<endl;
    return 0; 
}

D.参照による多態化
C++では,参照はポインタ以外にも多態効果を生み出す手段である.すなわち、ベースクラスの参照を使用して、派生クラスのインスタンスをバインドできます.
class Father;//  (  )
class Son:public Father{.....}//Son Father    
Son son;//son  Son     
Father &ptr=son;//                 

特に注意:
ptrは、派生クラスオブジェクトのベースクラスから継承されたメンバーにのみアクセスできます.ベースクラス(クラスFather)で定義されている虚関数があれば、派生クラス(クラスSon)でこの虚関数を書き換えることでクラスの多様性を実現することができる.
三、まとめ
1.参照の使用において、単純にある変数に別名をつけることは意味がない.参照の目的は主に関数パラメータの伝達に用いられ、大きなブロックデータやオブジェクトの伝達効率と空間の望ましくない問題を解決する.
2.伝達関数を参照するパラメータで、伝達中にパラメータがコピーされないことを保証し、伝達効率を高めるとともにconstの使用により、伝達中のパラメータの安全性を保証することができる
3.参照自体はターゲット変数またはオブジェクトの別名であり、参照に対する操作は本質的にターゲット変数またはオブジェクトに対する操作である.したがって、ポインタではなくリファレンスを使用できるようにします.