首先回顾一下C语言中的两种传递方式:
值传递和地址传递
优点 | 缺点 | |
---|---|---|
值传递 | 语法简洁、操作简单 | 需要值拷贝,如果对象较大,开销也大,不能直接操作外部变量。 |
地址传递 | 语法较为复杂 | 操作过程中容易遗漏取地址符号、或者解引用符号,但是传递时候只传递指针大小,效率较高,也能够直接操作数据。 |
//值传递
void ModifyValue(int a)
{
// 函数内部不能修改外部的值
}
//地址传递
void ModifyValue(int *a)
{
// 修改外部值的时需要解引用
*a = 20;
}
void test()
{
int num = 10;
ModifyValue(num);
ModifyValue(&num);
}
C++中的引用
引用是 C++ 对 C 的重要扩充。在 C/C++ 中指针的作用基本都是一样的,但是 C++ 增加了另外一种给函数传递地址的途径,这就是按引用传递(pass-by-reference),它也存在于其他一些编程语言中,并不是 C++ 的发明。
引用的语法
Type &ref = val;
引用规则:
- &在此不是求地址运算,而是起标识作用。
- 类型标识符是指目标变量的类型
- 必须在声明引用变量时进行初始化,初始化之后不能改变。
- 不能有 NULL 引用。必须确保引用是和一块合法的存储单元关联。
//1 . 引用语法
void test()
{
//引用一旦产生就必须马上初始化(关联合法对象)
int &ref1; //err
//引用不能指向NULL (有合法内存的可以放在=左边,没有合法内存的放在=右边)
int &ref2 = NULL; // err
// 类型标识符是指目标变量的类型
int v = 10;
//也就是说引用其实就是已有对象的别名
int &vr = v;
int b = 20;
vr = b; // 并非修改 vr 引用
// 可以对引用再建立引用, or vr v 地址一样
int &or = vr;
}
自定义类型引用
class Person
{
public:
string name;
int age;
}
void test()
{
Person p = {"wuyou",18};
Person &pRef = p;
cout<<(int)&p<<endl;
cout<<(int)&pRef<<endl;
}
对数组的引用
int array[] = {1,2,3,4};
int(&p)[4] = array;
//打印变量的类型
cout<<typeid(p).name()<<endl;
指针引用
- 在C语言中,我们如果要在函数内部修改外部变量,必须传递指针
- 引用所占空间大小与指针大小相同,只是这个过程由编译器内部实现,对用户不可见
语法:
& 左侧 变量类型
& 右侧 变量名
struct Person
{
public:
string name;
int age;
}
//C语法
int CreatePersonC(Person **p)
{
//判断参数...
//注意不能隐式类型转换
Person *p2 = (Person*)malloc(Person));
//判断申请的空间...
p2->age = 20;
*p = p2;
}
//C++语法
int CreatePersonCpp(Person* &p)
{
p = (Person*)malloc(Person));
//判断申请的空间...
p->age = 20;
}
void FreeCreatePersonCpp(Person* &p)
{
free(p);
p = NULL;
}
void test()
{
Person *p = NULL;
CreatePerson(&p);
CreatePersonCpp(p);
cout<<((p == NULL) ? "空":"非空")<<endl;
}
常量引用(万能引用)
作用:
- 避免函数内部修改外部数据。
- 可以对字面量进行引用。
- const
示例
//使用const修饰之后,内部不不能修改value的值
void printFunc(const int &value)
{
cout <<value<<endl;
}
void test()
{
//首先看看字面量的引用
int ref = 10;
int &ref1 = reg;
int &ref2 = 10; //err 原因是字面量没有内存
//万能引用,任何类型都可以引用
const int &ref3 = 10;
}
引用的机制
引用的本质就是通过指针来实现的
C++中的引用简化了指针的操作(取地址,解引用)
void test()
{
int a = 10;
int &ref = a; // int *const ref = &a;
ref = 100; //*ref = 100;
}
引用注意点
- 引用不产生新的变量(就是一个别名),减少形参与实参传递时的开销;
- 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用;
- 不存在空值的引用,意味着我们在使用引用的时候不需要判断是否为 NULL,一定程度上提升了效率;
- 引用传递的性质像是指针传递,但是书写方式像值传递,引用的语法要比指针的语法更加易读和操作,指针能做的事情,引用也能做.
- 最常见看见引用的地方是在函数参数和返回值中。如果从函数中返回一个引用,必须像从函数中返回一个指针一样对待。当函数返回值时,引用关联的内存一定要存在。
int &my_func()
{
int a = 10; //不要返回局部变量 err 原因,函数调用结束,函数函数从内存中销毁
static int a = 10;
return a;
}
void test()
{
//函数的返回值,如果是引用接,那就是别名
//函数的返回值,如果是值,那就值拷贝
int &result = my_func();
int ret = my_func();
cout <<(int)&result <<endl;
cout <<(int)&ret<<endl;
}