【从 C 向 C++ 进阶】- 对 C 的语法扩展 - 引用

本文详细介绍了C++中的引用概念,包括引用的性质、与指针的区别、引用与数组的关系以及const修饰的引用。引用作为已存在变量的别名,不分配额外内存,且必须在声明时初始化。引用不支持多级定义,引用数组和引用指针有特定解释。const引用可以创建只读变量,并允许不同数据类型的初始化,但需注意类型匹配。
摘要由CSDN通过智能技术生成

1. 引用简介

变量的本质是某段内存的名称,因此,可以把引用理解为该段内存的另一个名称。通俗来说,引用为已存在的变量的别名,是 C++ 为了优化指针而提出的新类型。

  • 示例:
int var = 0;
int& ref = var;

以上定义后,当改变 ref 的内容时,var 的内容同样被改变,这与指针操作内存相似。


2. 引用规则

引用遵循以下规则:

  • 引用本质上没有定义,而是一种关系型声明,声明它和已存在实体的关系。因此,引用类型必须与实体类型一致,且引用不再分配内存,地址与被引用的实体相同。
  • 引用声明时必须用实体进行初始化,表示该引用为该实体的别名。且声明后,无法变更引用的实体。
  • 可对某实体进行多次引用声明,表示该实体具备多个别名。
  • 可用引用对引用进行初始化,前者引用实际为前者引用的实体。
  • 引用除声明时需要使用 “&” 外,其余都不需要 “&” 表引用。其余时候的 “&” 作为取址符出现。
  • 实验:
int main()
{
	int var = 2;
	int& ref_A = var;
	
	cout << "var = " << var << endl;			// var = 2
	cout << "&var = " << &var << endl;			// &var = 0x7ffccea0922c
	cout << "ref_A = " << ref_A << endl;    	// ref_A = 2
	cout << "&ref_A = " << &ref_A << endl;	    // &ref_A = 0x7ffccea0922c
	
	int& ref_B = ref_A ;
	cout << "ref_B = " << ref_B << endl;	    // ref_B = 2
	cout << "&ref_B = " << &ref_B << endl;	    // &ref_B = 0x7ffccea0922c
	
	int tmp = 3;
	ref_B = tmp;						        // 仅是值的传递
	cout << "var = " << var << endl;			// var = 3
	cout << "ref_A = " << ref_A << endl;    	// ref_A = 3
	cout << "ref_B = " << ref_B << endl;	    // ref_B = 3
}

3. 引用与指针

引用与指针存在两种搭配:引用指针与指针引用。这就是中文拗口的地方,比较把两者混淆。其实两者的概念是不一样的:

  • 指针引用:指针为修饰词,即实际上为引用,这个引用的实体为指针。
  • 引用指针:引用为修饰词,即实际上为指针,指针是特殊数据类型变量,不能作为引用变量使用。因此,不能定义引用指针。注意,指向引用的指针与引用指针是不同的概念。

除了不存在引用指针外,引用的引用也是不存在的,因为引用只能进行一级定义。

  • 实验:
int main()
{
	int var = 2;
	int& ref_A = var;
	int* p = &ref_A;
	
	cout << "var = " << var << endl;			// var = 2
	cout << "&var = " << &var << endl;			// &var = 0x7fffa15199ec
	cout << "*p = " << *p << endl;				// *p = 2
	cout << "p = " << p << endl;				// p = 0x7fffa15199ec
	
	// 指针引用
	int*& p_ref = p;
	cout << "*p_ref = " << *p_ref << endl;	    // *p_ref = 2
	cout << "p_ref = " << p_ref << endl;    	// p_ref = 0x7fffa15199ec

	// 引用指针
	// int&* ref_p = p_ref;                     // error:cannot declare pointer to 'int &'
	
	// 引用的引用
	// int&& ref_B = ref_A;                     // error:expected unqualified-id before '&&' token
}

4. 引用与数组

与引用与指针的联系类似,引用与数组也存在两种搭配:引用数组与数组引用。比较把两者混淆,其实两者的概念是不一样的:

  • 引用数组:引用为修饰词,即实际上为数组,数组的每个元素为引用。引用数组是不存在的,可以用反证法证明。假设存在引用数组,由于引用在声明时必须初始化,因此在定义数组时必须对每个引用元素进行初始化,但引用声明的变量并不一定在一片连续的内存空间中,即每个引用元素的实际地址未必是连续的,而数组的本质便是一片连续的内存空间,这里明显产生了矛盾。因此,不存在引用数组这一概念。
  • 数组引用;数组为修饰词,即实际上为引用,引用的实体为数组。数组引用可用于函数传参为数组的情况,能避免在函数形参中数组被退化为指针,进而避免可能发生非法的内存访问。
  • 实验:
void print_arr_siz(int arr[], int (&arr_ref)[10])
{
	cout << "sizeof(arr) = " << sizeof(arr) << endl;            // sizeof(arr) = 8 (64位环境)
	cout << "sizeof(arr_ref) = " << sizeof(arr_ref) << endl;    // sizeof(arr_ref) = 40
}

int main()
{
	/* 数组引用 */
	int arr[10] = {0};
	int (&arr_ref)[10] = arr; 	
	cout << "&arr = " << &arr << endl;				            // &arr = 0x7ffcb99b0580
	cout << "&arr_ref = " << &arr_ref << endl;	                // &arr_ref = 0x7ffcb99b0580
	print_arr_siz(arr, arr);

	/* 引用数组 */
	int var_A = 0, var_B = 1;
	// int& ref_arr[2] = {var_A, var_B};                           // error:declaration of 'ref_arr' as array of references    
}

5. 引用本质

C++ 编译器在编译过程中使用指针常量作为引用的内部实现,引用所占用空间大小与指针相同。因此,引用的本质是一个指针常量,即:type & ref <==> typr * const ref

  • 实验:
struct Test
{
	int& ref_i;
	char& ref_c;
};

int main()
{
	cout << "sizeof(Test) = " << sizeof(Test) << endl;   // sizeof(Test) = 16 (64位机)
}

在业务的程序开发中,还是应该从引用的角度而非指针的角度对待使用引用。


6. const 修饰的引用

当使用 const 关键字修饰引用时,引用的一些特征发生了改变:

  • 可以使用字面量对 const 引用进行初始化,此时可以把引用符忽略,实际定义了一个新的只读属性的变量。
  • 当用已存在的变量对 const 引用初始化即仅是在普通引用声明前使用 const 修饰时,该引用为只读属性的引用,但不影响原本变量的读写属性。
  • 对于 const 引用,可以通过指针的方式进行读写,由于 C++ 为强类型语言,在指针的赋值时应使用强类型转换完成指针的赋值操作。
  • 可使用不同的数据类型的变量对 const 引用进行初始化,此时实际定义了一个新的只读属性的不同数据类型的变量,此时可能发生数据截断。
  • 实验:
int main()
{
    /* const 修饰普通的引用声明 */
	int var = 0;
	const int& ref = var;
	var = 1;
	// ref = 2;	// error: assignment of read-only reference 'ref'
	cout << "var = " << var << endl;			// var = 1
	cout << "ref = " << ref << endl;			// ref = 1
	cout << "&var = " << &var << endl;			// &var = 0x7fffe9b3ea74
	cout << "&ref = " << &ref << endl;			// &ref = 0x7fffe9b3ea74
	
	/* 用字面量对 const 引用初始化 */
	// int& ref_tmp = 5;	// error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
	const int& ref_con = 5;
	cout << "ref_con = " << ref_con << endl;	// ref_con = 5
	cout << "&ref_con = " << &ref_con << endl;	// &ref_con = 0x7fffe9b3ea78
	
	/* 指针访问 const 引用 */
	// int* p = &ref_con;	// error: invalid conversion from 'const int *' to 'int *'
	int* p = (int*)&ref_con;
	*p = 6;
	cout << "ref_con = " << ref_con << endl;	// ref_con = 6
	
	/* 不同数据类型的 const 引用声明 */
	double pi = 3.14;
    // int& pi_tmp = pi;	// error: invalid initialization of reference of type 'int&' from expression of type 'int'
    const int &ref_pi = pi;
	cout << "ref_pi = " << ref_pi << endl;		// ref_pi = 3
	cout << "&pi = " << &pi << endl;			// &pi = 0x7fffe9b3ea80
	cout << "&ref_pi = " << &ref_pi << endl;	// &ref_pi = 0x7fffe9b3ea7c	
}

更多从 C 向 C++ 进阶系列博文

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值