引用是c++对c的重要扩充。在c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递(pass-by-reference)
1. 引用基本使用
基本语法
Type& ref = val;
注意事项:
- &在此不是求地址运算,而是起标识作用。
- 类型标识符是指目标变量的类型
- 必须在声明引用变量时进行初始化。
- 引用初始化之后不能改变。
- 不能有NULL引用。必须确保引用是和一块合法的存储单元关联。
//1. 认识引用
void test01(){
int a = 10;
//给变量a取一个别名b
int& b = a;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "------------" << endl;
//操作b就相当于操作a本身
b = 100;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "------------" << endl;
//一个变量可以有n个别名
int& c = a;
c = 200;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "c:" << c << endl;
cout << "------------" << endl;
//a,b,c的地址都是相同的
cout << "a:" << &a << endl;
cout << "b:" << &b << endl;
cout << "c:" << &c << endl;
}
//2. 使用引用注意事项
void test02(){
//1) 引用必须初始化
//int& ref; //报错:必须初始化引用
//2) 引用一旦初始化,不能改变引用
int a = 10;
int b = 20;
int& ref = a;
ref = b; //不能改变引用
}
// 数组的引用
void test03()
{
int arr[] = {1,2,3,4,5};
// 方法1
// 1.定义数组类型
typedef int(MY_ARR)[5]; // 数组类型
// 2.建立引用
MY_ARR &arref1 = arr;
// 方法2
// 直接定义引用
int (&arref2)[5] = arr;
// 方法3
// 1.定义引用数组类型
typedef int(&MY_ARR_REF)[5];
// 2.建立引用
MY_ARR_REF arref3 = arr;
}
2. 引用的本质
引用的本质在c++内部实现是一个常指针.
Type& ref = val; // Type* const ref = &val;
C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见
// 发现是引用,转换为 int* const ref = &a
void testFunc(int &ref)
{
ref = 100; // ref 是引用,转换为 *ref = 100;
}
void test01()
{
int a = 10;
testFunc(a);
}
void test02()
{
int a = 10;
int &aRef = a; // 自动转换为 int * const ref = &a;这就说明引用为什么必须初始化
aRef = 20; // 内部发现aRef是引用,自动帮我们转换为:*aRef = 20;
}
3. 指针引用
指针的引用 是给指针变量取别名
在c语言中如果想改变一个指针的指向而不是它所指向的内容,函数声明可能这样:
void fun(int**);
// C语言 二级指针放一级指针变量的地址
// 被调函数
void func2(char **temp)
{
char *p;
p = (char *)malloc(64);
memset(p,0,64);
strcpy(p, "小花2");
*temp = p;
}
// 主调函数
void test02()
{
char* mp = NULL;
func2(&mp);
cout << mp << endl;
}
在C++中使用指针引用,去改变一个指针的指向,代码更加简洁
// C++
void func3(char* &temp) {
char *p;
p = (char *)malloc(64);
memset(p,0,64);
strcpy(p,"小花3");
temp = p;
}
void test03() {
char* mp= NULL;
func3(mp);
cout << mp << endl;
}
通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单:
- 函数调用时传递的实参不必加“&”符
- 在被调函数中不必在参数前加“*”符
4. 常量引用
常量引用的定义格式:
const Type& ref = val;
常量引用注意:
- 字面量不能赋给引用,但是可以赋给const引用
- const修饰的引用,不能修改。
void test01()
{
// 普通引用
int a = 10;
int &ref = a;
ref = 20;
// int &ref2 = 10; // 普通引用不能给字面量取名 错误
const int &ref3 = 10; // 可以给const修饰的引用赋予字面量
// const修饰符修饰的引用的原理
// 编译器会把上面的代码变为: int tmp = 10; const int &ref3=tmp
int b = 20;
const int &ref4 = b; // const修饰的引用,不能修改
// ref4 = 30;
}
5. 引用的使用场景
常量引用主要用在函数的形参,尤其是类的拷贝/复制构造函数。
将函数的形参定义为常量引用的好处:
- 引用不产生新的变量,减少形参与实参传递时的开销。
- 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。
如果希望实参随着形参的改变而改变,那么使用一般的引用,如果不希望实参随着形参改变,那么使用常引用。
//const int& param防止函数中意外修改数据
void ShowVal(const int& param){
cout << "param:" << param << endl;
}
引用作为函数参数传递数据,节约空间
// 1. 引用作为函数参数传递数据,节约空间
void func1(int &a, int &b)
{
int sum = a + b;
cout << "sum = " << sum << endl;
}
引用作为函数的返回值
int &func3() {
static int b = 10;
return b;
}
注意:
- 不要返回局部变量的引用
- 函数当左值,必须返回引用。
网友评论