一、问题
在对类创建出来的对象进行函数调用时,我发现我对引用的理解和掌握还不够深。这里决定再重新复习一下。
二、引用的本质
本质:引用的本质在c++内部实现是一个指针常量.
指针常量:指针的指向不可以修改,指针指向的值可以变。
当编译器发现某个引用时,会进行以下操作:
Example:
int a = 10;
int& ref = a;
发现是引用,转换为:
int* const ref = &a;
也就是说,这里的ref实质上是一个指针,里面存放了a的地址;
但是,ref被const修饰,就不再是指针变量,而是一个指针常量。
ref = 100;
ref是引用,转换为:
*ref = 100;
也就是说,我们操作的ref,其实就是指针常量解引用后指向的那个变量,那也就是a。
三、类和对象的引用问题
我们先看下面写的这段代码,解决第一个问题——参数引用问题:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
class Person
{
public:
int age;
public:
Person()
{
}
Person(int age)
{
this->age = age;
}
};
Person& BiggerPerson(Person& p1,Person& p2)
{
if (p1.age > p2.age)
{
p1.age++;
return p1;
}
else
{
p2.age++;
return p2;
}
}
int main()
{
Person p1(5);
Person p2(10);
cout << BiggerPerson(p1,p2).age<< endl;
return 0;
}
这里采用引用的方式,返回了两个人中年龄更大的那个人的姓名。
如果不加引用,得到的结果仍然是10;
但加了引用,
由于函数里的形参是实参的引用,指向的是同一个地址,
所以函数里的++会发生在实参身上。
输出的值就是11。
再看第二个问题:返回值。
这里有两种写法:
第一种:返回一个引用
Person& BiggerPerson(Person& p1,Person& p2)
{
if (p1.age > p2.age)
{
p1.age++;
return p1;
}
else
{
p2.age++;
return p2;
}
}
第二种:返回一个对象
Person& BiggerPerson(Person& p1,Person& p2)
{
if (p1.age > p2.age)
{
p1.age++;
return p1;
}
else
{
p2.age++;
return p2;
}
}
经过研究,这两种的区别在于:
对于下面这段代码的结果:
cout << BiggerPerson(BiggerPerson(p1, p2),p1).age << endl;
如果返回值是引用,
那么经过比较函数返回的新对象仍然可以作为左值,参与运算,并且最终结果为12。
如果返回值是对象,
那么编译器会报错:
为了看结果,我们把形参改为传值传参:
Person BiggerPerson(Person p1, Person p2)
{
if (p1.age > p2.age)
{
p1.age++;
return p1;
}
else
{
p2.age++;
return p2;
}
}
然后输出下面的代码:
Person p1(5);
Person p2(10);
cout << BiggerPerson(BiggerPerson(p1, p2),p1).age << endl;
cout << p2.age;
结果是12和10。
再看第三种情形——参数是传值参数,返回值是引用:
Person& BiggerPerson(Person p1, Person p2)
{
if (p1.age > p2.age)
{
p1.age++;
return p1;
}
else
{
p2.age++;
return p2;
}
}
这样的结果还是12和10。
这里返回的是p2的一份拷贝的引用,和原p2没有关系。
其实作用和情形2一样。
这样,区别就一目了然了:
1、如果参数是引用参数,那么返回值也需要使用引用类型的对象,这样才能一一对应,并且函数运算的结果可以作用到实参上;
2、如果参数是传值参数,那么返回值不管是引用还是传值,都影响不到实参。
四、结语
C++的引用真香!很多指针里的难点,引用都帮着简化了。但是这里还是非常建议学好指针,可以更好的理解底层原理。