(1)普通引用
1.引用的概念
变量名(标识符)回顾
- 变量名实质上是一段连续存储空间的别名,是一个标号(门牌号)
- 程序中通过变量来申请并命名内存空间
- 通过变量的名字可以使用存储空间
引出问题:对一段连续的内存空间只能取一个别名吗?
引用概念
- 引用可以看作一个已定义变量的别名,与原变量内存地址相同
- 引用的语法:Type &name = var;
#include <iostream>
using namespace std;
int main()
{
int a = 1;
int &b = a; //引用,b就是a的别名
b = 9;
cout << a << endl; // 9
a = 99;
cout << b << endl; // 99
cout << &a << "," << &b << endl; // 0x7ffece407cb4,0x7ffece407cb4
//可以看到地址是相同的
return 0;
}
2.引用与函数形参
- 普通引用在声明时必须用其它的变量进行初始化
- 引用作为函数参数声明时不进行初始化
- 引用做函数形参时,操作形参等价于操作实参
#include <iostream>
using namespace std;
// 2.函数形参无需初始化
void swap(int &a, int &b)
{
int c = 0;
c = a;
a = b;
b = c;
}
int main()
{
// 1.普通引用需初始化
int x = 1;
int &g = x;
// int &c; //这句会报错,因为普通引用,必须要初始化
int y = 100;
swap(x, y);
cout << x << " " << y << endl; //100 1
// swap函数形参是引用
// 因此函数内部进行改值操作后,x和y的值发生了变化
return 0;
}
3.引用与指针比较
- 引用的本质是指针。
- 引用作为其它变量的别名而存在,因此在一些场合可以代替指针。
- 引用相对于指针来说具有更好的可读性和实用性。
例子①:
#include <iostream>
using namespace std;
void myswap01(int a, int b) //不可以对实参进行交换
{
int c = 0;
c = a;
a = b;
b = c;
}
void myswap02(int *a, int *b) //可以对实参进行交换
{
int c = 0;
c = *a;
*a = *b;
*b = c;
}
void myswap03(int &a, int &b) //可以对实参进行交换
{
int c = 0;
c = a;
a = b;
b = c;
}
int main()
{
int x, y;
x = 10;
y = 20;
myswap01(x, y);
printf("x:%d , y:%d \n", x, y); //x:10 , y:20
myswap02(&x, &y);
printf("x:%d , y:%d \n", x, y); //x:20 , y:10
// a就是x的别名 b就是y的别名
myswap03(x, y);
printf("x:%d , y:%d \n", x, y); //x:10 , y:20
return 0;
}
例子②:
#include <iostream>
using namespace std;
struct Teacher
{
char name[64];
int age;
};
// 用指针
void changeT1(Teacher *pT)
{
// cout << pT->age << endl;
pT->age = 100;
}
// pT是t1的别名
void changeT2(Teacher &pT)
{
// cout << pT.age << endl;
pT.age = 33;
}
// pT和t1的是两个不同的变量
void changeT3(Teacher pT)
{
// cout << pT.age << endl;
pT.age = 45; //只会修改pT变量 ,不会修改t1变量 //因为是形参
}
int main()
{
Teacher t1;
t1.age = 18;
changeT1(&t1);
printf("t1.age:%d \n", t1.age); // 100
changeT2(t1); // pT是t1的别名
printf("t1.age:%d \n", t1.age); // 33
changeT3(t1); // pT是形参 ,t1 copy一份数据 给pT
printf("t1.age:%d \n", t1.age); // 33
return 0;
}
4.引用的本质
引用在C++中的内部实现是一个指针常量
#include <iostream>
using namespace std;
void modifyA1(int &a1)
{
a1 = 100;
}
void modifyA2(int *const a1)
{
*a1 = 200;
}
int main()
{
int a = 10;
modifyA1(a);
printf("a=%d \n", a); //a=100
a = 10;
modifyA2(&a);
printf("a=%d \n", a); //a=200
return 0;
}
5.结构体中有普通引用时占内存空间吗?
答案:占
#include <iostream>
using namespace std;
typedef struct Teacher1
{
int &a;
int &b;
}Teacher1;
typedef struct Teacher2
{
int x;
int y;
int &a;
int &b;
}Teacher2;
int main()
{
printf("%d\n", sizeof(Teacher1)); // 16
printf("%d\n", sizeof(Teacher2)); // 24
// 可以看出引用会占用内存
//int x=1, y=2;
//Teacher1 t1 = {x, y};
return 0;
}
6.函数返回值是引用
当函数返回值为引⽤时
- 若返回栈变量:不能作为左值使⽤,不能成为其它引⽤的初始值,也不要当作右值使⽤,容易出错,最好不要把栈变量的引⽤当作返回值
- 若返回静态变量或全局变量:既可作为右值使⽤,也可作为左值使⽤,可以成为其他引⽤的初始值。
- 当作右值时,返回该静态变量或全局变量的值
- 当作左值时,相当于就是返回的该静态变量或全局变量
示例代码理解
#include <iostream>
using namespace std;
int getA1()
{
static int a = 10;
a++;
return a;
}
int &getA2()
{
static int a = 10;
a++;
return a;
}
// 若返回静态变量或全局变量
// 可以成为其他引用的初始值
// 即可作为右值使用,也可作为左值使用
int main()
{
int a1 = 0;
int a2 = 0;
// 1.返回静态变量,当做右值
a1 = getA1();
cout << a1 << endl; //11
a2 = getA1();
cout << a2 << endl; //12
a1 = 100;
cout << a2 << endl; //12
// 2.返回静态变量的引用,当做右值
int &a3 = getA2();
cout << a3 << endl; //11
a3 = 100;
int &a4 = getA2();
cout << a4 << endl; //101
// 3.返回静态变量的引用,当做左值
getA2() = 1000;
cout << a3 << endl; //1000
cout << a4 << endl; //1000
return 0;
}
7.指针引用与二级指针
普通引用对标一级指针
指针引用对标二级指针
#include <iostream>
struct Teacher
{
char name[64];
int age;
};
// 二级指针做形参操作一级指针
int changeAge(Teacher **myp)
{
*myp = new Teacher;
(*myp)->age = 100;
return 0;
}
// 指针的引用
int changeAge2(Teacher *&myp)
{
myp = new Teacher;
myp->age = 18;
return 0;
}
int main()
{
Teacher *p = NULL;
changeAge(&p);
std::cout << p->age << std::endl; // 100
delete p;
changeAge2(p);
std::cout << p->age << std::endl; // 18
delete p;
return 0;
}
(2)const引用
1.常引用的含义
常引⽤:cost int &a = b;
作用含义:不能通过引⽤来改变那个地址的值,常引⽤让别名只拥有只读属性。但是可以通过指向常引用的指针来修改对应地址的值。
常引用通常用在函数形参中,让引用只有只读属性。
示例代码理解:
#include <iostream>
#include <cstring>
using namespace std;
class teacher
{
public:
char name[20];
int age;
void set_name(char *name)
{
strcpy(this->name, name);
}
void set_age(int age)
{
this->age = age;
}
};
void printTeacher(const teacher &teaName)
{
// teaName.age = 100; //非法,因为const参数,让teaName只有只读属性
cout << "teaName.age = " << teaName.age << endl;
cout << "teaName.name = " << teaName.name << endl;
}
int main()
{
int a = 10;
const int &b = a;
std::cout << b << std::endl; // 10
a = 11;
std::cout << b << std::endl; // 11
// 1.不用通过常引用变量来修改对应地址的值
// b = 100; //非法,因为常引用
// std::cout << b << std::endl;
// 2.但是可以通过指向常引用的指针来修改对应地址的值
int *p = (int *)&b;
*p = 100;
std::cout << a << std::endl; // 100
std::cout << b << std::endl; // 100
// 3.const让引用做函数参数时只有可读属性
teacher mytea;
strcpy(mytea.name, "tom");
mytea.set_age(20);
printTeacher(mytea);
return 0;
}
2.对字面量的普通引用和常引用
- 对字面量的普通引用,比如【int &c = 10;】,会error,因为字面量的地址是固定的,如果不加const编译失败
- 对字面量的常引用,比如[【const int &a = 10;】,ok的,因为字面量的地址固定正好与const的作用达成一致
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
//1.对字面量的普通引用,会error
//int &c = 10; //普通引用,引用一个字面量
//int &a1 = 19;
//会报错,因为字面量的地址是固定的,如果不加const编译失败
// 2.const变量只有只读属性
const int b = 10; //c++编译器把a放在符号表中
std::cout << &b << std::endl; // 0x7ffcd870aed0
// b = 100; // error,因为b是只读属性
// 3.对字面量的常引用, ok的
const int &a = 10;
std::cout << &a << std::endl; // 0x7ffcd870aed4 // c++编译器 会 分配内存空间
return 0;
}
3.常量引用是否占内存空间?
- ⽤变量对const引⽤初始化,const引⽤分配内存空间了吗? 没有,就是别名
- ⽤常量对const引⽤初始化,const引⽤分配内存空间了吗? 有,分配了内存空间
#include <iostream>
int main()
{
int a = 10;
const int &b = a;
std::cout << &a << "," << &b << std::endl; // 0x7fff40a1c448,0x7fff40a1c448
// 相等的,说明用变量对常量引用初始化的话,不会额外开辟内存空间
const int &c = 10;
std::cout << &c << std::endl; // 0x7fff40a1c44c
// 存在地址空间,说明⽤字面量对常量引⽤初始化,会分配内存空间
return 0;
}
end