天天看點

一文掌握C++引用的使用

引用變量

引用是已定義的變量的别名,引用變量和定義的變量都指向相同的值和記憶體單元。

建立引用變量

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;
}
           

注意

  1. 必須在聲明引用時将其初始化,而不能像指針那樣,先聲明,再指派。
  2. 引用一旦與某個對象關聯起來,就将一直效忠于它,而不像指針那樣,可以指向其他對象。

将引用用作函數參數

引用經常被用作函數參數,使得函數中的變量名成為調用程式中的變量的别名。是以傳遞引用時,函數将可以使用原始資料。

執行個體二

交換兩個變量的值

#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;
}

           

執行個體三程式說明

  1. display()隻顯示結構的内容,而不需要修改,是以使用const引用參數。對于這個函數而言,也可以按值傳遞結構,按值傳遞會進行副本的拷貝,是以相比按值傳遞,使用引用傳參可以節省時間和空間。
  2. 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);
               
  3. 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;         // 傳回局部變量,程式運作出現段錯誤
}
           

傳回引用的說明

  1. 函數傳回引用比傳回值效率更高。
  2. 傳回引用的函數實際上是被引用的變量的别名。
  3. 傳回引用時,應該避免傳回函數終止時不再存在的記憶體單元引用。
  4. 應盡量傳回const引用,避免傳回值被修改。

 為什麼要使用引用

  1. 傳遞給函數的參數需要被修改。
  2. 提高程式的運作速度,節省拷貝對象所需的時間和空間。

 函數傳參類型的選擇

  1. 如果資料對象很小,如内置資料類型或小型結構,則按值傳遞。
  2. 如果資料對象是數組,則使用指針。
  3. 如果資料對象是較大的結構,則使用指針或引用,以提高程式的效率。
  4. 如果資料對象是類對象,則使用引用。

繼續閱讀