C++的引用
- 1. 引用的概念
- 2. 语法规则
- 示例代码:三种常见引用的定义
- 2.1 指向普通变量的引用
- 示例代码:
- 2.2 指向指针的引用
- 2.3 指向数组的引用
- 2.4 示例代码 引用的使用:
- 3. 特点
- 3.1 引用仅仅只是变量的一个别名,不占用额外的内存空间
- 示例代码:
- 3.2 引用具备了传值和传地址的双重属性
- 示例代码:
- 3.3 引用必须在定义的时候立马初始化(例外的情况:引用作为参数除外)
- 示例代码:
- 3.4 引用初始化以后不可以被改变
- 示例代码:
- 3.5 void fun(int &x) // 此时实参只能是左值,不能是右值
- 示例代码:
1. 引用的概念
定义:变量的一个别名
- C语言中传参的方式总共两种: 传值,传地址
- C++中传参的方式总共三种: 传值,传地址,传引用
2. 语法规则
类型名 &引用的名字=变量名;比如: int a=45;int &b=a; // 定义了一个引用b,b指向a// 定义了一个引用b,b是a的别名float a=56.5;float &b=a;struct student a={"张三",18};struct student &b=a;
示例代码:三种常见引用的定义
#include <iostream>using namespace std; //使用命名空间std//公式:类型名 &引用的名字 = 变量名;int main(int argc, char const *argv[])
{//第一种:指向普通变量的引用double a = 45.6;double &b = a; // 定义引用cout<<"a的地址:"<<&a<<endl;cout<<"b的地址:"<<&b<<endl;//第二种:指向指针的引用int c = 45;int *p = &c;int *&x = p; // 定义引用cout<<"p的地址:"<<p<<endl;cout<<"x的地址:"<<x<<endl;char d[10] = "hello";char *q = d;char *&y = q; // 定义引用cout<<"q的地址:"<<(int *)q<<endl;cout<<"y的地址:"<<(int *)y<<endl;//第三种:指向数组的引用int e[5] = {45,96};// 第一种书写方式int (&z)[5]= e; // 定义引用cout<<"e的地址:"<<e<<endl;cout<<"z的地址:"<<z<<endl;// 第二种书写方式typedef int array[5]; // 给int [5] 取别名 为 array array &o = e; // 定义引用cout<<"e的地址:"<<e<<endl;cout<<"o的地址:"<<o<<endl;return 0;
}/*
执行结果:a的地址:0x7fff4d1e2090b的地址:0x7fff4d1e2090p的地址:0x7fff4d1e208cx的地址:0x7fff4d1e208cq的地址:0x7fff4d1e20eey的地址:0x7fff4d1e20eee的地址:0x7fff4d1e20d0z的地址:0x7fff4d1e20d0e的地址:0x7fff4d1e20d0o的地址:0x7fff4d1e20d0
*/
2.1 指向普通变量的引用
示例代码:
#include <iostream>using namespace std; //使用命名空间stdint main(int argc, char const *argv[])
{int a = 666;
/* 有两种叫法: 1、定义引用叫做b,b指向a2、定义引用叫做b,b是a的别名
*/ int &b = a; // 证明引用就是变量的别名==》打印地址cout<<"a的地址:"<<&a<<endl;cout<<"b的地址:"<<&b<<endl;cout<<"a的值:"<<a<<endl;cout<<"b的值:"<<b<<endl;return 0;
}/*
执行结果:a的地址:0x7fffc34d00dcb的地址:0x7fffc34d00dca的值:666b的值:666
*/
2.2 指向指针的引用
int a=89;
int *p=&a;
int *&b=p; //定义了指向p的引用
char buf[10]="hello";
char *q=buf;
char *&b=q;
2.3 指向数组的引用
int buf[10]; //类型 int[10]// 第一种表达方式
typedef int array[10];
array &b=buf;
// 第二种表达方式
int (&b)[5]= buf;
2.4 示例代码 引用的使用:
总结:只要是引用,写代码的时候,当成指向的变量去使用即可
#include <iostream>using namespace std; //使用命名空间std//公式:类型名 &引用的名字 = 变量名;int main(int argc, char const *argv[])
{//第一种:指向普通变量的引用double a = 45.6;double &b1 = a; // 定义引用// cout<<"a的地址:"<<&a<<endl;// cout<<"b的地址:"<<&b<<endl;//第二种:指向指针的引用int c = 45;int *p = &c;int *&b2 = p; // 定义引用// cout<<"p的地址:"<<p<<endl;// cout<<"x的地址:"<<x<<endl;char d[10] = "hello";char *q = d;char *&b3 = q; // 定义引用// cout<<"q的地址:"<<(int *)q<<endl;// cout<<"y的地址:"<<(int *)y<<endl;//第三种:指向数组的引用int e[5] = {45,96};// 第一种书写方式int (&b4)[5]= e; // 定义引用// cout<<"e的地址:"<<e<<endl;// cout<<"z的地址:"<<z<<endl;// 第二种书写方式typedef int array[5]; // 给int [5] 取别名 为 array array &o = e; // 定义引用// cout<<"e的地址:"<<e<<endl;// cout<<"o的地址:"<<o<<endl;// 引用的使用:只要是引用(无论什么类型),写程序时,当成指向的变量去使用即可//a怎么用,b1就照着写cout<<"修改前a的值: "<<a<<endl; cout<<"修改前b1的值: "<<b1<<endl;cout<<"修改前a的地址: "<<&a<<endl;cout<<"修改前b1的地址: "<<&b1<<endl;a=78.9;b1=28.9;cout<<"修改后a的值: "<<a<<endl; cout<<"修改后b1的值: "<<b1<<endl;cout<<"修改后a的地址: "<<&a<<endl;cout<<"修改后b1的地址: "<<&b1<<endl;//p怎么用,b2就照着写cout<<"p存放的地址: "<<p<<endl;cout<<"b2存放的地址: "<<b2<<endl;cout<<"*p存放的值: "<<*p<<endl;cout<<"*b2存放的值: "<<*b2<<endl;//e怎么用,b4就照着写cout<<"e[0] is: "<<e[0]<<endl;cout<<"b4[0] is: "<<b4[0]<<endl;return 0;
}/*
执行结果:修改前a的值: 45.6修改前b1的值: 45.6修改前a的地址: 0x7ffe22938920修改前b1的地址: 0x7ffe22938920修改后a的值: 28.9修改后b1的值: 28.9修改后a的地址: 0x7ffe22938920修改后b1的地址: 0x7ffe22938920p存放的地址: 0x7ffe2293891cb2存放的地址: 0x7ffe2293891c*p存放的值: 45*b2存放的值: 45e[0] is: 45b4[0] is: 45
*/
3. 特点
3.1 引用仅仅只是变量的一个别名,不占用额外的内存空间
ps:引用本质上也是用指针来实现的,底层也分配了存储空间
应用:引用作为函数的形参,作为函数的返回值,使用频率最高(提高程序运行效率)
示例代码:
#include <iostream>using namespace std; //使用命名空间std/* 使用引用:为了提高程序的运行效率
*/
typedef struct student{char name[10];int age;float score;char addr[100];
}student_t;void fun1(student_t stu)
{cout<<"传值成员地址"<<endl;cout<<"fun1中stu.name的地址:"<<(int *)(stu.name)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串cout<<"fun1中stu.age的地址:"<<&(stu.age)<<endl;cout<<"fun1中stu.score的地址:"<<&(stu.score)<<endl;cout<<"fun1中stu.addr的地址:"<<(int *)(stu.addr)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串
}void fun2(student_t &stu)
{cout<<"引用成员地址"<<endl;cout<<"fun2中stu.name的地址:"<<(int *)(stu.name)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串cout<<"fun2中stu.age的地址:"<<&(stu.age)<<endl;cout<<"fun2中stu.score的地址:"<<&(stu.score)<<endl;cout<<"fun2中stu.addr的地址:"<<(int *)(stu.addr)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串
}int main(int argc, char const *argv[])
{student_t stu = {"李1", 12, 99.9, "山卡拉"};cout<<"实参成员地址"<<endl;cout<<"实参中stu.name的地址:"<<(int *)(stu.name)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串cout<<"实参中stu.age的地址:"<<&(stu.age)<<endl;cout<<"实参中stu.score的地址:"<<&(stu.score)<<endl;cout<<"实参中stu.addr的地址:"<<(int *)(stu.addr)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串fun1(stu); // 传值fun2(stu); // 传引用return 0;
}/*
执行结果:可以看出引用传参的地址与实参成员地址相同,减少栈空间的使用,提高程序运行效率实参成员地址实参中stu.name的地址:0x7ffcb1bd5860实参中stu.age的地址:0x7ffcb1bd586c实参中stu.score的地址:0x7ffcb1bd5870实参中stu.addr的地址:0x7ffcb1bd5874传值成员地址fun1中stu.name的地址:0x7ffcb1bd57d0fun1中stu.age的地址:0x7ffcb1bd57dcfun1中stu.score的地址:0x7ffcb1bd57e0fun1中stu.addr的地址:0x7ffcb1bd57e4引用成员地址fun2中stu.name的地址:0x7ffcb1bd5860fun2中stu.age的地址:0x7ffcb1bd586cfun2中stu.score的地址:0x7ffcb1bd5870fun2中stu.addr的地址:0x7ffcb1bd5874
*/
3.2 引用具备了传值和传地址的双重属性
即修改引用的形参相当于修改该地址的值
示例代码:
#include <iostream>using namespace std; //使用命名空间std/* 使用引用:为了提高程序的运行效率
*/
typedef struct student{char name[10];int age;float score;char addr[100];
}student_t;void fun1(student_t stu)
{stu.age = 22;// cout<<"传值成员值"<<endl;// cout<<"fun1中stu.name的值:"<<(int *)(stu.name)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串// cout<<"fun1中stu.age的值:"<<&(stu.age)<<endl;// cout<<"fun1中stu.score的值:"<<&(stu.score)<<endl;// cout<<"fun1中stu.addr的值:"<<(int *)(stu.addr)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串
}void fun2(student_t &stu)
{stu.age = 32;// cout<<"fun2中stu.name的值:"<<(int *)(stu.name)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串// cout<<"fun2中stu.age的地址:"<<&(stu.age)<<endl;// cout<<"fun2中stu.score的地址:"<<&(stu.score)<<endl;// cout<<"fun2中stu.addr的地址:"<<(int *)(stu.addr)<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串
}int main(int argc, char const *argv[])
{student_t stu = {"李1", 12, 99.9, "山卡拉"};cout<<"实参成员值(修改前)"<<endl;cout<<"实参中stu.name的值:"<<stu.name<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串cout<<"实参中stu.age的值===========:"<<stu.age<<endl;cout<<"实参中stu.score的值:"<<stu.score<<endl;cout<<"实参中stu.addr的值:"<<stu.addr<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串fun1(stu); // 传值cout<<"实参成员值(传值后)"<<endl;cout<<"实参中stu.name的值:"<<stu.name<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串cout<<"实参中stu.age的值===========:"<<stu.age<<endl;cout<<"实参中stu.score的值:"<<stu.score<<endl;cout<<"实参中stu.addr的值:"<<stu.addr<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串fun2(stu); // 传引用cout<<"实参成员值(传引用后)"<<endl;cout<<"实参中stu.name的值:"<<stu.name<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串cout<<"实参中stu.age的值===========:"<<stu.age<<endl;cout<<"实参中stu.score的值:"<<stu.score<<endl;cout<<"实参中stu.addr的值:"<<stu.addr<<endl; // cout中字符数组需要强转成int*才可打印地址,否则输出的是字符串return 0;
}/*
执行结果:实参成员值(修改前)实参中stu.name的值:李1实参中stu.age的值===========:12实参中stu.score的值:99.9实参中stu.addr的值:山卡拉实参成员值(传值后)实参中stu.name的值:李1实参中stu.age的值===========:12实参中stu.score的值:99.9实参中stu.addr的值:山卡拉实参成员值(传引用后)实参中stu.name的值:李1实参中stu.age的值===========:32实参中stu.score的值:99.9实参中stu.addr的值:山卡拉
*/
3.3 引用必须在定义的时候立马初始化(例外的情况:引用作为参数除外)
int a=78;
int &b;
b=a; //语法错误
void swap3(int &a,int &b) 例外的情况:引用作为函数形参例外
{}
示例代码:
#include <iostream>using namespace std; //使用命名空间stdint main(int argc, char const *argv[])
{int a = 1;int &b;b = a;return 0;
}
编译时报错:
3.4 引用初始化以后不可以被改变
int a=78;
int b=98;
int &c=a; //c是a的别名
c=b; //不要理解成引用c重新指向b,应该理解为把b的值赋值给了a(引用c是a的别名)
示例代码:
#include <iostream>using namespace std; //使用命名空间stdint main(int argc, char const *argv[])
{int a = 1;int c = 2;//定义引用b,b是a的别名int &b = a;//b是a的别名,就永远都是a的别名//这个特点跟引用底层实现有关,因为引用的底层是用 int *const p;指针实现b = c; // 等价于 a=c// 验证cout<<"a="<<b<<endl;return 0;
}/*
执行结果:a=2
*/
3.5 void fun(int &x) // 此时实参只能是左值,不能是右值
fun(a); // a是左值,正确
fun(88); // 88不是左值,错误
fun(a+a); // a+a是右值,错误
void fun(const int &x) //此时实参可以是左值/右值
常应用:
const修饰的引用叫做常引用
常引用:作用1:防止引用修改指向的变量值作用2:实参传递的时候既能传递左值,也能传递右值int buf[8]={10,12};int otherbuf[8]buf=14; //错误,数组名不能作为左值otherbuf=buf; //错误,数组名不能作为左值buf=buf+1; //错误,数组名不能作为左值int * const&b=buf; //数组名只能作为右值,此时引用必须用const修饰才能指向右值int a=67;const int &c=a; //等价于int const&c=a;作用3:如果是常量,定义引用,必须使用常引用const int a=78;const int &b=a;
示例代码:
#include <iostream> //C++的标准输入输出流头文件
using namespace std; //我要使用命名空间std
/*引用作为函数的形参:实参传递左值和右值概念:const修饰的引用叫做常引用例如: int a=45;const int &b=a;常引用的作用:作用1:函数的形参是常引用,实参可以传递左值也可以传递右值函数的形参是普通引用(非const修饰),实参只可以传递左值不能传递右值作用2:常引用不可以修改指向的变量值
*///void fun(int &num)
void fun(const int &num)
{cout<<"num is: "<<num<<endl;
}
int main()
{int n=456;//调用函数//写法1:fun(n);//写法2:/*error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’*/fun(n+1); //n+1是右值,普通引用不能传递右值//写法3:fun(147); //147也是右值 ,普通引用不能传递右值return 0;
}