【引用】——串讲(视频89-94)
Note:
i.视频为黑马程序员C++视频,系列文章为视频听课笔记;
ii.引用不仅包含定义及简单应用,在类与对象…中也有涉及;
iii.难度指数:+++
iv.不论变量、函数名、标识符形式怎样复杂,只要我们考虑编程的本质是对内存的操作,对内存进行分析,一切逻辑都会变得清晰。
1.引用的基本语法
1)含义:给变量起别名
2)code格式:数据类型 &别名 = 变量名
例如:int &b = a
,则b为变量a的引用,也是对引用b的初始化
3)内存图解:
![定义引用,则建立内存关系](https://img-blog.csdnimg.cn/0655e11bab434a7a8a3f4130e9539ce8.png)
由图可见,若定义b为变量a的引用,则b可对变量a中存储的值进行操作。
2.引用的注意事项
1)引用定义则必须进行初始化
2)引用在第一次初始化后不可更改
3.引用做函数参数(重点)
之前提到某封装函数被调用,传参时的传递方式分为按值传递或按地址传递,前者形参的改变不能改变实参的值,后者形参的改变可以改变实参的值;或者说前者形参不能修饰实参,后者形参可以修饰实参。
实际上,按地址传递实际上是传递的是指针,其记录实参的地址,因此可以实现对实参的修饰。引入“引用”的概念之后,可利用其作为函数参数实现对实参的修饰。
至此,函数被调用时,参数的传递方式有以下几种:
1)值传递
2)地址传递
3)引用传递
举例:以完成实参值的交换为例
1)值传递
#include<iostream>
#include<string>
using namespace std;
void swap01(int a,int b)
{
int temp = a;
a = b;
tmep = b;
cout << "值传递形参a=" << a << endl;
cout << "值传递形参b=" << b << endl;
}
int main()
{
int a = 10;
int b = 20;
swap01(a, b);
cout << "值传递交换后实参a=" << a << endl;
cout << "值传递交换后实参b=" << b << endl;
}
依靠值传递交换函数,无法实现实参的交换。
2)地址传递
#include<iostream>
#include<string>
using namespace std;
void swap02(int *a,int *b)
{
int temp = *a;
*a = *b;
tmep = *b;
cout << "地址传递形参a=" << a << endl;
cout << "地址传递形参b=" << b << endl;
}
int main()
{
int a = 10;
int b = 20;
swap02(&a, &b);
cout << "地址传递交换后实参a=" << a << endl;
cout << "地址传递交换后实参b=" << b << endl;
}
依赖地址传递交换函数,可以实现实参的交换。
3)引用传递
#include<iostream>
#include<string>
using namespace std;
void swap03(int &c,int &d)
{
int temp = c;
c = d;
tmep = d;
cout << "引用传递形参a=" << c << endl;
cout << "引用传递形参b=" << d<< endl;
}
int main()
{
int a = 10;
int b = 20;
swap03(a, b);
cout << "引用传递交换后实参a=" << a << endl;
cout << "引用传递交换后实参b=" << b << endl;
}
依赖引用传递交换函数,可以实现实参的交换,形参作了实参的引用。
【总结】:
1)封装函数在没有返回值的情况下,要想实现对main函数中实参的操作,必须在封装函数中对其内存进行操作。
2)引用类似于指针的简化版本,它不像指针一样类似于一种数据类型,需要定义,有固定的含义,即存储的是内存地址。它更像是变量的分身,代替变量完成不能完成的对内存的操作。
4.引用做函数返回值(重点、难点)
Note:
1)不能返回局部变量的引用,但可以返回静态变量的引用。
【碎碎念】:总之,所有的注意事项都跟内存息息相关。
代码示例:
#include<iostream>
#include<string>
using namespace std;
int& test01()
{
int a = 10;
return a;
}
int& test02()
{
static int a = 100;
return a;
}
int main()
{
int &ref = test01();
cout << "作局部变量引用ref的值=" << ref << endl;
cout << "作局部变量引用ref的值=" << ref << endl;
int &ref2 = test02();
cout << "作静态变量引用ref的值="<< ref2 << endl;
cout << "作静态变量引用ref的值="<< ref2 << endl;
}
2)函数的调用可以作为左值
代码示例:
#include<iostream>
#include<string>
using namespace std;
int& test02()
{
static int a = 100;
return a;
}
int main()
{
int &ref2 = test02();
cout << "ref2="<< ref2 << endl;
cout << "ref2="<< ref2 << endl;
test02() = 1000;
cout << "ref2="<< ref2 << endl;
cout << "ref2="< ref2 << endl;
}
从上面的例子中可以看到,函数test02()作为左值可以实现对静态变量的赋值操作。
5.引用的本质
1)定义:引用的本质是一个指针常量。
涉及到指针常量:指针的指向不可更改,但指向的内存中的值可更改。
易混淆的常量指针:指针的指向可以更改,但指向的值不可更改。
2)前面在引用的注意事项中提到,引用一旦初始化就不能更改,这也是跟引用的本质有关系,引用初始化时就确定了其指向,而其指向是不可更改的。
3)之所以引用的定义形式等跟指针不同,是因为在C++中,编译器对引用进行了包装,代码示例如下:
#include<iostream>
#include<string>
using namespace std;
void func(int &ref)
{
ref = 100;
}
int main()
{
int a = 10;
int &ref = a;
ref = 20;
cout << "ref:" << ref << endl;
cout << "a:" << a << endl;
func(a);
cout << "ref:" << ref << endl;
cout << "a:" << a << endl;
return 0;
}
6.引用在类与对象中的应用(拓展、难点)
1)背景:创建类person
,已知其中包含age
属性以及personaddperson
成员函数,该成员函数可以实现两个实例对象age
属性的求和。
2)要求:创建实例对象p1
、p2
,实现p2.age
与若干个p1.age
的连续相加。
3.1)示例介绍:
如代码示例一所示,成员函数personaddperson
的函数类型为person
,从函数的输出结果中可以看到,每次调用成员函数时,this指针都指向不同的实例对象(从输出的地址可以看出),因此,代码示例一不能实现p2.age
与若干个p1.age
的连续相加,仅且只能实现一次相加,下一次调用时,this指针即指向由成员函数创建的新的实例对象(非p2
)。
class person
{
public:
person(int age)
{
this->age = age;
}
person personaddperson(person &p)
{
this->age += p.age;
cout << "this指针" << this << endl;
cout << "this指向的年龄为:" << this->age << endl;
return *this;
}
int age;
};
void test01()
{
person p1(10);
person p2(10);
p2.personaddperson(p1).personaddperson(p1).personaddperson(p1);
cout << "p2对象年龄为:" << p2.age << endl;
}
int main()
{
test01();
}
当成员函数返回为类,输出结果为:
![在这里插入图片描述](https://img-blog.csdnimg.cn/5c7a8d69df4c4bc08cbf40468c4998d6.png)
3.2)示例介绍
如代码示例二所示,成员函数personaddperson
的函数类型为person&
,从函数的输出结果中可以看到,每次调用成员函数时,this指针均指向实例对象p2
(从输出的地址可以看出)。这可以理解为:当利用类的引用作为函数返回类型时,所有创建的示例对象都是p2
的别名,因此实现了对p2
内存的连续操作。因此,示例二可以实现p2.age
与若干个p1.age
的连续相加。return *this
实际上返回的就是this指针指向的实例对象。
person& personaddperson(person &p)
{
this->age += p.age;
cout << "this指针" << this << endl;
cout << "this指向的年龄为:" << this->age << endl;
return *this;
}
当成员函数返回为“类”的引用(代码示例如上),输出结果为:
![在这里插入图片描述](https://img-blog.csdnimg.cn/0082e4690bf340a084235a26b6724d94.png)
【一点思考】:
1)在刚开始学习引用时,把引用单纯的勉强理解为变量的别名,当接触了引用在“类与对象”中的应用后,发现,引用可以是任何事物的别名,可以是类的别名,好像也可以是函数的别名等等。在本文6)的案例中,其作为p2实例对象的别名实现了对p2中age属性的连续操作。
2)之前我将指针可以看作一种数据类型,记为int/char... *
,即可套用其他数据类型定义的模板。
同理,引用也可以看作一种数据类型,记为int/char/person &
,这样可以帮助我们更好的理解代码。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)