参考ブログの一部: http://t.csdn.cn/b5x99
関数パラメータとしての参照
機能: 関数がパラメーターを渡すとき、参照テクニックを使用して、仮パラメーターによって実際のパラメーターを変更させることができます。
利点: 実際のパラメータを変更するためのポインタを簡素化できる
値の受け渡し: 関数が実パラメータを呼び出すと、その値は仮パラメータに渡されます。値が渡されるときに、仮パラメータは実パラメータを変更できません。
アドレスの受け渡し: 関数が呼び出されるとき、実パラメータはアドレスを仮パラメータに渡します。
//1. 值传递
void mySwap01(int a, int b) {
int temp = a;
a = b;
b = temp;
}
//2. 地址传递
void mySwap02(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
//3. 引用传递
void mySwap03(int& a, int& b) {//注意这里,传参数时就是对引用的初始化
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
mySwap01(a, b);
cout << "a:" << a << " b:" << b << endl;
mySwap02(&a, &b);
cout << "a:" << a << " b:" << b << endl;
mySwap03(a, b);
cout << "a:" << a << " b:" << b << endl;
system("pause");
return 0;
}
概要: パラメータを参照で渡すことの効果は、アドレスで渡すことと同じです。引用の構文がより明確かつシンプルになりました
関数の戻り値として参照
参照は関数の戻り値として存在できます。参照を関数の戻り値として使用する場合は、関数を定義するときに関数名の前に & を追加する必要があります
以下は 4 つの状況の比較分析です。
fn1 と fn2 の 2 つの関数があり、temp はグローバル変数です
//代码来源:RUNOOB
#include<iostream>
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<<b<<endl;//78.5
cout<<c<<endl;//78.5
cout<<d<<endl;//78.5
return 0;
}
ケース 1: 戻り値を指定して関数を呼び出す
グローバル変数 temp の値を返すとき、C++ はメモリ内に一時変数を作成し、 temp の値を一時変数にコピーします。メイン関数 main に戻った後、代入ステートメント a=fn1(5.0) は一時変数の値を変数 a にコピーします。
ケース 2: 関数の戻り値で参照を初期化して関数を呼び出す
この場合、関数 fn1() は値で戻りますが、戻るときは、まず temp の値を一時変数 にコピーします。main 関数に戻った後、一時変数を使用して参照変数 b を初期化し、b が一時変数 のエイリアスになります。一時変数のスコープは短いため(C++ 標準では、一時変数またはオブジェクトのライフサイクルは完全なステートメント式が終了した後、つまりステートメント float &b=fn1(5.0); の後で終了します)、および b です。一時変数のエイリアスであるため、b は無効になる危険があり、将来の値が不定の値になる可能性が非常に高くなります。
ケース 3: 参照を返して関数を呼び出す
この場合、関数 fn2() は参照によって戻り、temp 自体を返します。コピーは生成されませんが、変数 temp を main 関数に直接返します。つまり、main 関数の代入ステートメント内の lvalue です。これは変数 temp から直接コピーされます (つまり、c はエイリアスではなく変数 temp の単なるコピーです)、したがって一時変数の生成が回避されます。特に変数 temp がユーザー定義クラスのオブジェクトである場合、クラス内でコピー コンストラクターを呼び出してメモリ内に一時オブジェクトを作成するプロセスも回避され、プログラム内の時間とスペースの効率が向上します。
ケース 4: 関数によって返された参照を新しい参照の初期化値として使用して関数を呼び出す
この場合、関数 fn2() の戻り値はコピーを生成せず、変数 temp を main 関数に直接返します。main 関数では参照宣言 d が戻り値で初期化されます。つまり、このとき d は変数 temp の別名になります。temp はグローバル変数であるため、d の有効期間中は常に有効なままであるため、このアプローチは安全です。
概要: 関数の戻り値として参照を使用する最大の利点は、戻り値のコピーがメモリ内に生成されないことです。
また、ローカル変数参照を返さないように注意する必要があります。
//返回局部变量引用
int& test01() {
int a = 10; //局部变量
return a;
}
//返回静态变量引用
int& test02() {
static int a = 20;//全局区
return a;
}
int main() {
//不能返回局部变量的引用
int& ref = test01();
cout << "ref = " << ref << endl;//ref=10
cout << "ref = " << ref << endl;//乱码
//如果函数做左值,那么必须返回引用
int& ref2 = test02();
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
test02() = 1000;
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
system("pause");
return 0;
}
test01 関数を呼び出すと、最初の出力は正常ですが、2 番目の出力は文字化けします
また、関数の戻り値が参照の場合、関数呼び出しを左辺値として使用できます。