引用變量
引用是已定義的變量的别名,引用變量和定義的變量都指向相同的值和記憶體單元。
建立引用變量
C和C++ 使用 & 符号來訓示變量的位址,但是C++ 給 & 符号賦予了另一個含義,将其用來聲明引用。例如:
int a;
int & b = a; // 将b作為b的别名,指向相同的記憶體單元
執行個體一
引用的使用
#include <iostream>
using std::cout;
using std::endl;
int main()
{
int a = 100;
int &b = a; // b是a的引用
cout << "a = " << a << endl; // 100
cout << "b = " << b << endl; // 100
b++;
// a和b的值是相同的
cout << "a = " << a << endl; // 101
cout << "b = " << b << endl; // 101
// a和b的位址是相同的
cout << "a的位址: " << &a << endl;
cout << "b的位址: " << &b << endl;
return 0;
}
注意
- 必須在聲明引用時将其初始化,而不能像指針那樣,先聲明,再指派。
- 引用一旦與某個對象關聯起來,就将一直效忠于它,而不像指針那樣,可以指向其他對象。
将引用用作函數參數
引用經常被用作函數參數,使得函數中的變量名成為調用程式中的變量的别名。是以傳遞引用時,函數将可以使用原始資料。
執行個體二
交換兩個變量的值
#include <iostream>
using std::cout;
using std::endl;
// 按引用傳參
void swap_by_reference(int &a, int &b);
// 按指針傳參
void swap_by_point(int *a, int *b);
// 按值傳參
void swap_by_value(int a, int b);
int main()
{
int a = 10;
int b = 20;
cout << "調用swap_by_reference交換前" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
// 按引用傳參,交換成功
swap_by_reference(a, b);
cout << "調用swap_by_reference交換後" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << endl;
a = 10;
b = 20;
cout << "調用swap_by_point交換前" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
// 按指針傳參,交換成功
swap_by_point(&a, &b);
cout << "調用swap_by_point交換後" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << endl;
a = 10;
b = 20;
cout << "調用swap_by_value交換前" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
// 按值傳參,交換失敗
swap_by_value(a, b);
cout << "調用swap_by_value交換後" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
void swap_by_reference(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void swap_by_point(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void swap_by_value(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
臨時變量、引用參數和const
如果實參與引用參數不比對,C++ 将生成臨時變量。目前,僅當參數為const引用時,C++才允許傳入與引用參數不比對的值。
何時生成臨時變量
如果引用參數是const,下面兩種情況将生成臨時變量:
- 實參的類型正确,但不是左值。
- 實參的類型不正确,但可以轉換為正确的類型。
#include <iostream>
void test_func(double &a)
{
std::cout << a << std::endl;
}
int main()
{
double a = 10.1;
int b = 10;
test_func(a + 0.9); // 報錯
test_func(b); // 報錯
return 0;
}
可以通過常量引用解決上述報錯問題
#include <iostream>
// 接受一個常量引用參數
void test_func(const double &a)
{
std::cout << a << std::endl;
}
int main()
{
double a = 10.1;
int b = 10;
test_func(a + 0.9); // 實參類型正确,但不是左值,生成臨時變量
test_func(b); //實參與形參類型不比對, 生成臨時變量
return 0;
}
上述情況下,編譯器都将生成一個臨時匿名變量,確定實參不被改變,并讓參數a指向這個臨時變量。這些臨時變量隻在函數調用期間存在,函數調用結束後,會将其删除。
盡可能使用const
- 使用const可以避免無意中修改資料的程式設計錯誤。
- 使用const使函數能夠處理const和非const實參,是否将隻能接受非const資料。
- 使用const引用使函數能正确生成并使用臨時變量。
将引用用于結構
引用非常适合用于結構、類等自定義資料類型。
struct Student
{
std::string m_name;
int m_math;
int m_English;
int m_Chinese;
int m_total_score;
};
Student stu1;
Student &stu2 = stu1; // 定義結構體引用
執行個體三
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
struct Student
{
std::string m_name;
int m_math;
int m_English;
int m_Chinese;
int m_total_score;
};
// 列印學生資訊
void display(const Student &stu);
// 計算所有科目總成績,儲存在另一個結構體中,并傳回一個結構體引用
Student &accumulate_all_students(Student &all_stu, const Student &stu1);
// 計算總成績
// 引用傳參
void accumulate_total_score(Student &stu);
// 指針傳參
void accumulate_total_score(Student *stu);
int main()
{
Student stu1 = {"zhangsan", 89, 90, 78};
Student stu2 = {"lisi", 86, 98, 68};
Student stu3 = {"wangwu", 67, 93, 88};
Student stu4 = {"zhaoliu", 99, 76, 76};
Student all_stu = {"The whole class", 0, 0, 0};
accumulate_total_score(&stu1);
accumulate_total_score(stu2);
accumulate_total_score(stu3);
accumulate_total_score(stu4);
display(stu1);
cout << endl;
display(stu2);
cout << endl;
display(stu3);
cout << endl;
display(stu4);
cout << endl;
// display(accumulate_all_students(all_stu, stu1));
Student &test = accumulate_all_students(all_stu, stu1);
display(all_stu);
cout << endl;
accumulate_all_students(all_stu, stu2) = stu1; // 都變為stu1
display(all_stu);
cout << endl;
display(test);
return 0;
}
void display(const Student &stu)
{
cout << "姓名: " << stu.m_name << endl;
cout << "數學: " << stu.m_math << endl;
cout << "英語: " << stu.m_English << endl;
cout << "國文: " << stu.m_Chinese << endl;
cout << "總成績: " << stu.m_total_score << endl;
}
Student &accumulate_all_students(Student &all_stu, const Student &stu1)
{
if(all_stu.m_name.empty())
{
all_stu.m_name = "The whole class";
}
all_stu.m_math += stu1.m_math;
all_stu.m_English += stu1.m_English;;
all_stu.m_Chinese += stu1.m_Chinese;;
all_stu.m_total_score += stu1.m_total_score;
return all_stu;
}
void accumulate_total_score(Student &stu)
{
stu.m_total_score = stu.m_math + stu.m_Chinese + stu.m_English;
}
void accumulate_total_score(Student *stu)
{
stu->m_total_score = stu->m_math + stu->m_Chinese + stu->m_English;
}
執行個體三程式說明
- display()隻顯示結構的内容,而不需要修改,是以使用const引用參數。對于這個函數而言,也可以按值傳遞結構,按值傳遞會進行副本的拷貝,是以相比按值傳遞,使用引用傳參可以節省時間和空間。
- accumulate_total_score()計算一個學生的總成績。需要傳遞一個結構體引用,而按值傳遞則不可行。也可以使用指針參數,但要複雜一點:
void accumulate_total_score(Student *stu) { stu->m_total_score = stu->m_math + stu->m_Chinese + stu->m_English; } accumulate_total_score(&stu1);
- accumulate_all_students()接受兩個結構參數,并傳回一個引用。将第二個結構體的資料加入到第一個結構體中。是以,參數一為引用,參數二為const引用。如果傳回的不是引用,将将傳回all_stu的拷貝。但傳回的是引用,是以傳回的是最初傳遞給accumulate_all_students()的Student對象。
傳回引用的用法
display(accumulate_all_students(all_stu, stu1));
// 等效于
accumulate_all_students(all_stu, stu1);
display(all_stu);
accumulate_all_students(accumulate_all_students(all_stu, stu1), stu2);
// 等效于
accumulate_all_students(all_stu, stu1);
accumulate_all_students(all_stu, stu2);
// 将stu1賦給了all_stu。
// 如果函數按值傳回,則這條語句不能通過編譯。
// 如果傳回const引用這條語句也不能編譯通過。
accumulate_all_students(all_stu, stu3) = stu1;
const Student &return_local_reference(Student &stu)
{
Student new_stu = stu; // 局部變量,函數結束,局部變量的記憶體将會被釋放
return new_stu; // 傳回局部變量,程式運作出現段錯誤
}
傳回引用的說明
- 函數傳回引用比傳回值效率更高。
- 傳回引用的函數實際上是被引用的變量的别名。
- 傳回引用時,應該避免傳回函數終止時不再存在的記憶體單元引用。
- 應盡量傳回const引用,避免傳回值被修改。
為什麼要使用引用
- 傳遞給函數的參數需要被修改。
- 提高程式的運作速度,節省拷貝對象所需的時間和空間。
函數傳參類型的選擇
- 如果資料對象很小,如内置資料類型或小型結構,則按值傳遞。
- 如果資料對象是數組,則使用指針。
- 如果資料對象是較大的結構,則使用指針或引用,以提高程式的效率。
- 如果資料對象是類對象,則使用引用。