【データ構造】c ++リファレンスについて

転載ソース:http://www.cnblogs.com/xiaofengkang/


ビッグガイの要約:クォートの利点の1つは、関数が呼び出されたときにメモリにコピーが生成されないことです。

見積もり概要

(1)参照の使用において、変数に単にエイリアスを与えることは意味がありません。参照の目的は、不十分な転送効率と、関数パラメーターの転送におけるデータまたはオブジェクトの大きなブロックのスペースの問題を解決するために主に使用されます
(2)関数パラメーターを参照で渡すと、パラメーターの受け渡し中にコピーが生成されないため、転送の効率が向上します。constを使用すると、参照転送の安全性が保証されます。
(3)参照とポインタの違い
<1>ポインタは、ポインタ変数を介してオブジェクトを指すと、それが指す変数を間接的に操作します。プログラムでポインタを使用すると、プログラムの可読性が低下します。
<2>参照自体がターゲット変数のエイリアスであり、参照の操作はターゲット変数の操作です。
(4)引用を使用するタイミング。フロー演算子<<および>>、代入演算子=の戻り値、コピーコンストラクターのパラメーター、代入演算子=のパラメーター、およびその他の状況では、参照を使用することをお勧めします。

参照は変数(ターゲット)のエイリアスであり、参照に対する操作は、変数に対する直接操作とまったく同じです。

参照される宣言方法
类型标识符 &引用名=目标变量名;

【例1】:
int a;  
int &ra=a;  //定义引用ra,它是变量a的引用,即别名

(1)&ここは住所計算用ではなく、識別用です。
(2)タイプ識別子は、ターゲット変数のタイプを参照します。
(3)参照を宣言するときは、同時に初期化する必要があります。
(4)参照が宣言された後、ターゲット変数名はターゲットの元の名前と参照名の2つの名前を持つことに相当し、参照名は他の変数名のエイリアスとして使用できません。

ra=1; 	//等价于 a=1; 

(5)参照の宣言は変数の新しい定義ではなく、参照名がターゲット変数名のエイリアスであり、それ自体がデータ型ではないため、参照自体がストレージユニットを占有せず、システムが参照を割り当てない記憶装置。したがって、参照のアドレスを検索することは、ターゲット変数のアドレスを検索することです。&raは&aと同じです。
(6)配列の参照を確立できません。配列はいくつかの要素のコレクションであるため、配列のエイリアスを作成することはできません。
(7)参照への参照を確立できず、参照へのポインタを確立できません。参照はデータ型ではないためです!したがって、参照への参照、参照へのポインタはありません。

【例如】:
int n;
int &&r=n;//错误,编译系统把"int &"看成一体,把"&r"看成一体,即建立了引用的引用,引用的对象应当是某种数据类型的变量
int &*p=n;//错误,编译系统把"int &"看成一体,把" *p "看成一体,即建立了指向引用的指针,指针只能指向某种数据类型的变量

(8)ポインタ参照を作成できることは言及する価値があります

【例如】:
int *p;
int *&q=p;//正确,编译系统把" int * "看成一体,把"&q"看成一体,即建立指针p的引用,亦即给指针p起别名q。

参照アプリケーション

1.パラメータとしての参照

参照の重要な役割は、関数パラメーターとしてです。以前のC言語では、関数パラメーターの転送は値の転送でした。データの大きなブロックがパラメーターとして渡される場合、使用される解決策は多くの場合ポインターです。これにより、スタックにデータのブロック全体がプッシュされなくなり、プログラムの効率が向上します。しかし、現在(C ++では)同等に効率的なオプション(および一部の特殊なケースでは必要なオプション)が追加されています。これは参照です。

【例2】:
void swap(int &p1,  int &p2) {
    
    //此处函数的形参p1, p2都是引用 
	int p;
	p=p1;
	p1=p2;
	p2=p;
} 

プログラムでこの関数を呼び出すために、対応するメイン呼び出し関数の呼び出しポイントを、実際のパラメーターとしての特別な要件なしに、変数を実際のパラメーターとして直接呼び出すことができます。
たとえば、上記で定義されたswap関数に対応して、対応するメインの呼び出し関数は次のように書くことができます。

main(){
    
     
 int a,b;
 cin>>a>>b; 			//输入a,b两变量的值
 swap(a,b); 			//直接以变量a和b作为实参调用swap函数 
 cout<<a<< ' ' <<b; 	//输出结果 
}
上述程序运行时,如果输入数据10 20并回车后,则输出结果为20 10。

由【例2】可看出:
(1)関数への参照を渡すことの効果は、ポインターを渡すことと同じです。このとき、呼び出された関数の仮パラメーターは、使用する元の呼び出し関数の実際のパラメーター変数またはオブジェクトのエイリアスになるため、呼び出された関数の仮パラメーター変数の操作は、(呼び出し関数内の)対応するターゲットオブジェクトに対して行われます。呼び出し関数内)。
(2)関数のパラメーターを渡すための参照の使用は、メモリ内の実際のパラメーターのコピーを生成しません。これは、実際のパラメーターに対する直接操作です。関数のパラメーターを渡すための一般変数の使用、関数呼び出しが発生した場合、仮パラメーターにストレージを割り当てる必要があります。ユニット、仮パラメーター変数は実際のパラメーター変数のコピーです。オブジェクトが渡されると、コピーコンストラクターも呼び出されます。したがって、パラメーターによって渡されるデータが大きい場合、効率と参照が占めるスペースは一般変数よりも優れています。
(3)関数パラメーターとしてポインターを使用しても参照を使用する効果を得ることができますが、呼び出された関数では、ストレージユニットも仮パラメーターに割り当てる必要があり、計算のために「*ポインター変数名」の形式を繰り返し使用する必要があります。これは、プログラムのエラーと読みやすさの低下を引き起こしがちですが、一方で、呼び出し元の関数の呼び出しポイントでは、変数のアドレスを実際のパラメーターとして使用する必要があります。引用は使いやすく、明確です。

void fun(int * a,int * b){
    
    
	int t = (*a);
	*a = *b;
	*b = t;
}

対応するキー関数は次のとおりです。

int main(){
    
    
	int i = 1,j = 2;
	fun(&i,&j);
	cout<<i<<" "<<j<<endl;
	return 0;
}

参照を使用してプログラムの効率を向上させるだけでなく、関数に渡されるデータが関数内で変更されないように保護する場合は、定数参照を使用する必要があります。

2、よく引用される

しばしば引用された宣言

const 类型标识符 &引用名=目标变量名;

この方法で宣言された参照は、参照によってターゲット変数の値を変更できないため、参照のターゲットがconstになり、参照の安全性が達成されます。

【例3】: 
int a ;
const int &ra=a;
ra=1;		//错误,不能通过引用对目标变量的值进行修改
a=1;		//正确 

これは、コードをより堅牢にするだけでなく、他のニーズもあります。

【例4】:假设有如下函数声明:
string foo();
void bar(string & s);

その場合、次の式は無効になります。

bar(foo());
bar("hello world"); 

その理由は、foo()と "hello world"文字列の両方が一時オブジェクトを生成し、C ++ではこれらの一時オブジェクトがconst型であるためです。したがって、上記の式はconst型オブジェクトを非const型に変換する試みであり、これは不正です。

引用型参数应该在能被定义为const的情况下,尽量定义为const

3.戻り値として参照

関数値を参照で返すには、関数定義は次の形式にする必要があります:説明:(1)関数値を参照で返し、関数を定義するときに関数名の前に&を追加します。(2)関数値を参照で返す最大の利点は、戻り値のコピーはメモリに作成されません。
类型标识符 &函数名(形参列表及类型说明)
{函数体}



【例5】: 以下程序中定义了一个普通的函数fn1(它用返回值的方法返回函数值),另外一个函数fn2,它以引用的方法返回函数值。

#include <iostream.h>

float  temp;				//定义全局变量temp
float  fn1(float r);		//声明函数fn1
float  &fn2(float r);		//声明函数fn2
float  fn1(float r);		//声明函数fn1

float  fn1(float r){
    
     		//定义函数fn1,它以返回值的方法返回函数值
	temp=(float)(r*r*3.14); 
	return temp; 
}

float &fn2(float r){
    
     		//定义函数fn2,它以引用方式返回函数值
	temp=(float)(r*r*3.14); 
	return temp;
}

void main() {
    
     				//主函数
	float a=fn1(10.0); 		//第1种情况,系统生成要返回值的副本(即临时变量)
	float &b=fn1(10.0); 	//第2种情况,可能会出错(不同 C++系统有不同规定)
 //不能从被调函数中返回一个临时变量或局部变量的引用
	float c=fn2(10.0); 		//第3种情况,系统不生成返回值的副本
 //可以从被调函数中返回一个全局变量的引用
	float &d=fn2(10.0); 	//第4种情况,系统不生成返回值的副本
 //可以从被调函数中返回一个全局变量的引用
	cout<<a<<c<<d;
} 

引用作为返回值,必须遵守以下规则:
(1)ローカル変数への参照を返すことはできません。この記事では、Effective C ++ [1]のItem 31を参照できます。主な理由は、関数が戻った後にローカル変数が破棄されるため、返された参照が「非参照」参照になり、プログラムが不明な状態になるためです。
(2)関数内でnewによって割り当てられたメモリへの参照を返すことはできません。この記事では、Effective C ++ [1]のItem 31を参照できます。ローカル変数の受動的な破壊はありませんが、この状況(関数内に新しく割り当てられたメモリへの参照を返す)では、他の厄介な状況に直面します。たとえば、関数から返された参照が一時変数としてのみ表示され、実際の変数に割り当てられていない場合、この参照が指す(newによって割り当てられた)スペースを解放できず、メモリリークが発生します。
(3)クラスメンバーへの参照を返すことができますが、constが最適です。この原則は、Effective C ++ [1]の項目30を参照できます。主な理由は、オブジェクトの属性が特定のビジネスルールに関連付けられている場合、その割り当ては他の属性やオブジェクトの状態に関連していることが多いため、割り当て操作をビジネスルールにカプセル化する必要があるためです。他のオブジェクトが属性の非定数参照(またはポインター)を取得できる場合、属性の単純な割り当てはビジネスルールの整合性を破壊します。
(4)参照と一部の演算子のオーバーロード:
ストリーム演算子<<および>>。これらの2つの演算子は、連続して使用されることがよくあります。例:cout << "hello" << endl;したがって、これらの2つの演算子の戻り値は1であり、引き続きこれらの2つの操作をサポートする必要がありますシンボルのストリーム参照。その他のオプションのソリューションには、ストリームオブジェクトを返す、ストリームオブジェクトポインターを返すなどがあります。ただし、ストリームオブジェクトを返すためには、プログラムは新しいストリームオブジェクトを再構築(コピー)する必要があります。つまり、2つの連続する<<演算子は実際には異なるオブジェクト用です!これは受け入れがたい。ストリームポインタを返す場合、<<演算子は継続的に使用できません。したがって、ストリームオブジェクト参照を返すことが唯一のオプションです。この唯一の選択は非常に重要であり、参照の重要性とかけがえのないものを示しています、おそらくこれが参照の概念がC ++言語で導入された理由です。代入演算子=。この演算子は、ストリーム演算子と同様に、継続的に使用できます(例:x = j = 10;または(x = 10)= 100;代入演算子の戻り値は、代入できるように左辺値でなければなりません)。したがって、参照はこの演算子の唯一の戻り値オプションになります。

【例6】 测试用返回引用的函数值作为赋值表达式的左值。
#i nclude <iostream.h>
int &put(int n);
int vals[10];
int error=-1;
void main(){
    
    
put(0)=10;  //以put(0)函数值作为左值,等价于vals[0]=10; 
put(9)=20;  //以put(9)函数值作为左值,等价于vals[9]=10; 
cout<<vals[0]; 
cout<<vals[9];
}


int &put(int n){
    
    
	if (n>=0 && n<=9 )
		return vals[n]; 
	else 
		cout<<"subscript error"; return error;
}

(5)他の一部の演算子では、参照を返さない:+-* / 4つの算術演算子。彼らは参照を返すことはできません。EffectiveC ++ [1]のItem 23はこの問題を詳細に説明しています。主な理由は、これらの4つの演算子には副作用がないためです。そのため、これらの演算子はオブジェクトを戻り値として構築する必要があります。オプションのソリューションには、オブジェクトを返す、ローカル変数への参照を返す、newによって割り当てられたオブジェクトへの参照を返す、および静的オブジェクト参照。前述の3つの参照ルールによる戻り値によると、2番目と3番目のオプションはどちらも拒否されました。((a + b)==(c + d))は常にtrueになるため、静的オブジェクトへの参照でもエラーが発生します。したがって、残された唯一のオプションは、オブジェクトを返すことです。

4.引用と多態性

参照は、ポリモーフィックな効果を生み出すことができるポインタ以外の別の手段です。つまり、基本クラスへの参照は、その派生クラスのインスタンスを指すことができます。

【例7】: 
class A;
class B:public A{
    
    ……};
B b;
A &Ref = b;  // 用派生类对象初始化基类对象的引用

Refは、基本クラスから継承された派生クラスオブジェクトのメンバーにアクセスするためにのみ使用でき、基本クラス参照は派生クラスを指します。仮想関数がクラスAで定義されていて、仮想関数がクラスBで書き換えられている場合、多態性効果はRef。

おすすめ

転載: blog.csdn.net/qq_44714521/article/details/107006806
おすすめ