导读
接着上一篇的内容继续学习,今天我们需要重点学习引用。
1. 引用
在C++中,引用是一种特殊的变量,用于别名一个已经存在的对象或变量。通过引用,可以使用别名来操作原始对象,而不是创建一个新的副本。
 引用提供了一种简洁和高效的方式来传递参数、返回值和修改变量的值。
1.1 引用特性
- 引用在定义时必须初始化
 - 一个变量可以有多个引用
 - 引用一旦引用一个实体,再不能引用其他实体
 
void TestRef()
{
	int a = 10;
	int& ra = a;//<====定义引用类型
	printf("%p\n", &a);
	printf("%p\n", &ra);
}
int main()
{
	TestRef();
	return 0;
}
 

上述代码我们可以发现两者的地址是相同的,a和r指向是完全一样的
 注意:
 引用类型必须和引用实体是同种类型的
1.2 做参数使用
引用作为函数参数,意味着在函数调用时,我们将一个变量的引用传递给函数。这样,函数可以直接操作原始变量,而不是对其进行拷贝。
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}
int main() {
    int x = 10;
    int& ref = x; // 引用x
    cout << "x = " << x << endl;   // 输出: x = 10
    cout << "ref = " << ref << endl; // 输出: ref = 10
    ref = 20; // 修改ref,实际上是修改x
    cout << "x = " << x << endl;   // 输出: x = 20
    cout << "ref = " << ref << endl; // 输出: ref = 20
    int a = 5;
    int b = 10;
    swap(a, b); // 传递a和b的引用,修改原始变量
    cout << "a = " << a << endl;   // 输出: a = 10
    cout << "b = " << b << endl;   // 输出: b = 5
    return 0;
}
 

1.3 做返回值使用
函数返回引用时需要确保返回的引用仍然指向有效的内存空间。通常,可以返回类成员变量的引用、静态变量的引用、函数内静态局部变量的引用等。
 同时要注意的是,返回引用时需要避免返回对局部变量的引用,因为局部变量在函数结束后会被销毁,返回对其引用可能会导致未定义行为。
int& Max(int& a, int& b)
{
    return (a > b) ? a : b;
}
int main()
{
    int x = 10, y = 20;
    int& max = Max(x, y);
    max = 30;  // 修改了y的值
    cout << "y: " << y << endl;  // 输出: 30
    return 0;
}
 

 我们再来看一下下面的代码:
int& Add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int& ret1 = Add(1, 2);
	int& ret2 = Add(3, 4);
	cout << "Add(1, 2) is :" << ret1 << endl;
	cout << "Add(3, 4) is :" << ret2 << endl;
	return 0;
}
 
按照我们的预期,我们输出的两个数应该是3和7,然而:
 
 这是为什么呢?
因此,应该避免将局部变量作为返回值的引用类型返回。
1.4 引用和指针的区别
- 指针使用*来定义,而引用使用&来定义。
 
int* ptr; // 声明一个指针
int& ref = *ptr; // 定义一个引用,引用指针所指向的变量
 
- 指针可以被初始化为null或指向任意变量的地址,而引用必须在声明时初始化,并且不能为null。
 
int* ptr = nullptr; // 指针为空
int x = 5;
int& ref = x; // 引用x,ref指向x的地址
 
- 指针可以被重新赋值为指向其他变量的地址,而引用一旦初始化后就不能重新赋值为引用其他变量。
 
int x = 5;
int y = 10;
int* ptr = &x; // ptr指向x的地址
ptr = &y; // ptr现在指向y的地址
int& ref = x; // ref引用x
ref = y; // 修改了x的值,ref仍然引用x
 
- 指针可以为空指针,即指向空地址,而引用不可以为空。
 
int* ptr = nullptr; // 指针为空
int& ref; // 错误,引用必须初始化
 
- 有多级指针,但是没有多级引用
 
1. 5 const引用
常引用可以引用普通变量,可以引用常变量,可以引用字面变量。
 我们来看下面示例:
常变量:
const int a = 10;
int& ra = a;		// 错误
const int& ra = a;	//正确
 
 
字面值:
int& b = 10;		// 错误
const int& b = 10;	// 正确
 
 
类型转换:
double d = 12.34;
int& rd = d;			// 错误
const double& rd = d;	// 正确
 
 
将 const 修饰符用于 int 类型的引用,可以确保在引用对象上不会进行修改操作,保护对象的不可变性,避免意外的修改导致数据不一致或错误的计算结果。
常引用的声明方式与普通引用相同,只是在引用类型前添加const关键字。常引用主要用于函数参数传递和对象成员访问,以确保访问的对象不会被修改。
void Print(const int& value) 
{
    // value 为 const 引用,不能修改其值
    cout << "Value: " << value << endl;
    // value = 10;  // 错误,不能修改 const 引用的值
}
int main() 
{
    int num = 5;
    Print(num);  // 传递 num 的值给 printValue 函数
    return 0;
}
 
2. 内联函数
以inline修饰的函数叫做内联函数,是C++中的一种函数,它的定义和调用都被嵌入到调用该函数的地方,而不是通过函数调用的机制进行调用,没有函数调用建立栈帧的开销。
 内联函数的主要目的是为了提高函数的执行效率,减少函数调用的开销。
inline int add(int a, int b) {
    return a + b;
}
int main()
{
    int ret = add(10, 20);
    return 0;
}
 
内联函数的使用有以下几点需要注意:
3. auto关键字
auto关键字是C++中的一个关键字,用于声明变量时自动推导变量的类型。
 使用auto关键字可以省略变量类型的声明,编译器会根据变量的初始化表达式推导出变量的类型。
 例如:
auto a = 10; // a的类型为int 
auto b = 3.14; // b的类型为double 
auto c = "hello"; // c的类型为const char*
 
 
注意:
 auto关键字在编译器推导类型时是静态的,即编译时就确定了类型,无法在运行时动态改变变量的类型。
auto与指针和引用结合起来使用:
当auto与指针和引用结合使用时,auto会推导出指针或引用的类型。
int x = 10;
auto *ptr = &x; // ptr的类型为int*
float y = 3.14;
auto *ptr2 = &y; // ptr2的类型为float*
 
使用auto声明引用类型变量示例:
int x = 10;
auto &ret = x; // ref的类型为int&
float y = 3.14;
auto &ret2 = y; // ref2的类型为float&
 
4. 基于范围的for循环
基于范围的for循环是一种简化的循环结构,用于遍历一个序列(如字符串、列表、元组等)中的每个元素。
语法形式如下:
变量表示当前迭代的元素,序列表示需要遍历的序列,循环体表示需要执行的操作。
 我们常用下面的这种方式来遍历数组:
int main()
{
	int arr[] = { 1, 2, 3, 4, 5 };
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
		arr[i] *= 2;
	for (int* p = arr; p < arr + sizeof(arr) / sizeof(arr[0]); ++p)
		cout << *p << endl;
	return 0;
}
 
今天我们来看范围for的使用。
int main()
{
	int arr[] = { 1, 2, 3, 4, 5 };
	for (auto& e : arr)
		e *= 2;
	for (auto e : arr)
		cout << e << " ";
	return 0;
}
 
注意:
- for循环迭代的范围必须是确定的
 - 迭代的对象要实现++和==的操作。
 










