引用
简单的来说,所谓引用就是给已定义变量重新起一个名字(起一个外号)。
创建一个引用变量
C++使用 &符号 作为引用(和取地址符是同一个符号)
int a ;
int & b = a;
上述语句就是给 int类型的变量a起了一个b的别名。
我们发现,当& 在等号左边(我们也可以叫其为 “左值”)的时候是作为引用出现的。当& 符号在等号右边(同样也可以称其为 “右值”)0的时候是作为取地址符出现的。
我们在使用引用的时候要注意几个要点:
1. 必须在声明引用时进行初始化。
int a;
int & b;
b = a;//不能这样做!
更加直白的说。和const指针很像(虽然还并没有说道const指针,可以暂时了解一下,等知道const指针后,再回来看看)
一旦引用和某个变量关联起来,就会一直在这个变量身边。
站在编译器的角度来理解就是:int & b = a;//这是你写的语句
在编译器眼中:int * const ptr = &a;//这是编译器中的语句
这个 *ptr 就和 b 是一样的。
2.修改了引用的值,变量的值也跟着修改。
简单的理解就是:我们给这个变量仅仅是起了另外一个名字,它还是它。无论是a还是b都是那个变量自己。
实际上,这个通过引用起的外号和原来的名字是指向同一块空间
3.引用必须指向一块合法的空间(不可以用NULL引用)。
void test()
{
// int &ref = 10;//引用了 不合法的内存
const int &ref = 10;//加入const,编译器处理方式:int tmp = 10; const int &ref = tmp;
//tmp临时空间我们看不到
//ref = 10;
int* p = (int*)&ref;//通过指针找到这个内存空间
*p = 10000;。//修改
cout<<"ref = "<<ref<<endl;
}
以上 const int &ref = 10;
是常量的引用。
如果加入了const就无法修改值。
尤其是void showValue(const int &val);
如果编译器发现值被改变了,就会报错
4.引用不能重名
数组的引用
int arr[10];
for(int i = 0; i < 10; i++)
{
arr[i] = i;
}
//给数组其别名的方式
int (&pArr)[10] = arr;
我们可以用上述的方式来给数组也起一个别名。
这里有一点需要注意:没有引用的数组!
这样做是不合法的,也是没有意义的。
有关引用的应用(*重点)
这里主要是说明引用和函数是如何配合使用的。
1. 将引用作为函数的参数
在这里要先回忆一下 函数的传值方式:
- 值传递
- 指针传递
- 引用传递(C++新增)
有关值传递,应该大学学C语言的时候老师就应该强调过了,在这里请回忆起来值传递为什么不能交换值的原因。做一个比喻:老师把复习的资料传给学生,学生们把资料修改了。但是老师那里的那份资料并没有被修改。
所以我们为了交换值,引入的指针传值,因为是对地址的操作所以值成功的交换了。
这里的引用传递,其本质还是对地址的操作,所以也是可以交换值的。
//值传递
void mySwap01(int a ,int b)
{
int tmp = a ;
a = b;
b = tmp;
}
//地址传递
void mySwap(int* a ,int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//引用传递
void mySwap03(int &a,int &b)//&a = a,&b=b
{//可以避免使用指针
int tmp = a ;
a = b;
b = tmp;
}
其实有的时候,引用传值的还是有限制的。
int square(int &a)
{ //计算平方
return a*a;
}
以上程序,如果我们在main中调用的时候int b = square(x + 5)
其实这样做是不允许的,因为x+5并不是一个变量,这里的参数a不过只是一个别名。但是编译器不一定会报错,但是编译器会指出这一点:
Warning:Temporary used for parameter 'a' in call to square(int &)
我们可以解释一下编译器是怎么帮你渡过这个难关的:其实,这里编译器给你建立了一个临时变量,并将其初始化表达式 x+5 的值。然后,a成为了该临时变量的引用。这样就对这种“不检点的行为”睁一只眼闭一只眼。(其实编译器为你做了不少好事)
有关编译器为程序员创建临时变量的事情,详情参见《C++ Primer Plus》这里就不详细描述了。
2. 将引用用于结构体
引用的引入主要是为了这种用户自定义的类型。
在这个部分,要介绍三个点:
1.使用指向结构体的引用
2.将引用作为函数返回值
3.使用函数调用来访问结构体的成员
请看以下程序:
struct sys
{
char a[10];
char b[20];
int c;
};
const sys & func(sys & s)
{
s.c++;
return s;
}
int main()
{
sys s1 =
{
"hello!",
"world!",
1
};//给结构体赋值
func(s1);
sys copy;
copy = func(s1);
return 0;
}
1.使用指向结构体的引用func(s1);
该函数调用结构体s1 按引用传递 传给func()函数
2.将引用作为返回值
通常,返回机制将返回值复制到临时存储区中,随后调用程序将访问这片存储区。但是,如果是返回引用的话就不需要这个临时存储区,函数实际上是直接访问返回值。
通常,引用将指向传递给函数的引用,因此调用函数实际上直接访问了自己的一个变量。copy = func(s1);
如果函数func()返回一个结构体,sys的内容被复制到一个临时的存储区中,然后返回存储单元的内容 复制给copy中。但是,由于是返回一个指向s1的引用,这种情况下s1的内容就被直接复制到copy中,也不需要临时存储空间。
这就是引用的优点:更高效
总结一下就是:返回引用的函数实际上被引用变量的别名。
但是这里我们需要注意的是:避免返回当函数终止时已经不存在的内存单元,如果需要的话使用 new 动态开辟空间,记得 delete 或者 delete[] 释放。
3.使用函数调用来访问结构体的成员
如果我们想输出s1的值,我们可以这样做:cout << func(s1).c
其实以上的语句 和 以下语句等价;func(s1);
cout << s1.c << endl;
3.有关指针的引用
指针的引用这个概念是针对双指针而提出的。
struct Person
{
int Age;
};
void allocatMemory(Person ** p)// **p 具体的Person对象, *p 对象的指针, p指针的指针
{
*p = (Person*)malloc(sizeof(Person));
(*p)->Age = 100;
}
//利用指针引用来开辟空间
void allocateMemoryByRef(Person* &p)
{
p = (Person*)malloc(sizeof(Person));
p->Age = 1000;
}
这样对指针进行引用可以避免混淆。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。