1.变量名
- 变量名实质上是一段连续存储空间的别名,是一个标号(门牌号)
- 通过变量来申请并命名内存空间
- 通过变量的名字可以使用存储空间
问题:对一段连续的内存空间只能取一个别名吗?
2.引用的概念
变量名,本身是一段内存的引用,即别名(alias).引用可以看做是一个已定义变量的别名。
引用的语法: Type &name = var;
int main()
{
int a = 10;//在内存上开辟4个字节,a就是代表这4个字节内存的别名
int *p = &a;//让一个指针 指向这4个字节的空间
*p = 20;
//C++提供了另一种对变量的间接操作的方式,就是使用引用
int &b = a;//定义一个引用b指向a,b此时就是变量a的别名
b = 40;
cout<<"a="<<a<<endl;//输出a=40
cout<<"b="<<b<<endl;//输出b=40
cout<<"&a="<<&a<<endl;
cout<<"&b="<<&b<<endl;//地址一样
}
3.规则
- 引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型与原类型保持一致,且不分配内存。与被引用的常量有相同的地址。
- 声明的时候必须初始化,一经声明,不可变更。
- 可对引用,再次引用。多次引用的结果,就是某一变量具有多个别名。
- &符号前有数据类型时,是引用。其他皆为地址。
int &c;//只是单纯定义一个引用,不初始化,会报错!引用一定要初始化,除了作参数
int printA(int &a)//引用作为函数参数的时候,不需要初始化,因为形参一定会被赋值
{
cout<<"a="<<a<<endl;
return 0;
}
4.引用作为函数参数
struct teacher
{
int id;
char name[64];
}
void printT(Teacher t)//局部临时变量,会发生值拷贝
{
cout<<"id="<<t.id<<endl;
cout<<"name="<<t.name<<endl;
t.id = 100;//修改失败!
}
void printT2(Teacher *t)
{
cout<<"id="<<t->id<<endl;
cout<<"name="<<t->name<<endl;
t->id = 100;//修改成功
}
void printT3(Teacher &t)//Teacher &t = t1;t就是t1的一个别名
{
cout<<"id="<<t.id<<endl;
cout<<"name="<<t.name<<endl;
t.id = 10;//修改成功
}
-------------------------------------------------------------------
void my_swap(int a,int b)//无法实现两数据交换
{
int temp = a;
a = b;
b = temp;
}
void my_swap1(int *a,int *b)//开辟了两个指针空间实现交换
{
int temp = *a;
*a = *b;
*b = temp;
}
void my_swap2(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
int main(void)
{
Teacher t1 = {1,"shang3"};
printT(t1);
cout<<t1.id<<endl;//输出为1
printT2(&t1);
cout<<t1.id<<endl;//输出为100
printT3(t1);
cout<<t1.id<<endl;//输出为10
int a = 10;
int b = 20;
my_swap(a,b);
cout<<"a="<<a<<" "<<"b="<<b<<endl;//交换不成功
my_swap1(&a,&b);
cout<<"a="<<a<<" "<<"b="<<b<<endl;//成功
my_swap2(a,b);
cout<<"a="<<a<<" "<<"b="<<b<<endl;//成功
}
5.引用的意义
- 引用作为其他变量的别名而存在,因此在一些场合可以代替指针
- 引用相对于指针来说具有更好的可读性和使用性
int main(){
int a = 10;
int b = 20;
int &re = a;//引用必须被初始化
//引用在初始化之后,不能够被改变
re = b;//a=b 而不是让 re引用指向b
cout<<"re="<<re<<endl;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
//输出re=20,a=20,b=20
re = 100;
cout<<"re="<<re<<endl;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
//输出re=100,a=100,b=20
}
6.引用的本质
- 引用在C++中的内部实现是一个常指针
Type &name <=====> Type * const name - C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占的空间大小与指针相同
- 从使用角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏。
在研究引用的时候,可以将引用理解为一个常指针
在理解引用的时候,可以将引用理解为一个变量的别名
void motifyA(int * const a)//常指针,也是一个常量,也是必须要初始化,也不能被修改
{
*a = 100;
}
void motifyB(int &a)
{
a = 1000;//a实际上是一个常量指针,但是如果你给a赋值,编译器会有一个隐形的 * 操作
}
struct Teacher
{
int a;
char name[64];
};
//Teacher &t没有作类型拷贝,只开辟了4字节内存用来指向传进来的便来
//Teacher t 的话就是拷贝了传进来的参数!
void motifyTeacher(Teacher &t)
{
t.id = 100;
//如果说t是一个常指针,*t就是指针指向的内存空间 (*t).id=100;
}
int main(void)
{
int a = 20;
motifyA(&a);
cout<<"a = "<<a<<endl;//100
motifyB(a);
//int value --> int &a , int &a = value给引用指定指向哪个变量的时候,编译器提供又一个隐形的操作
//a = &value;
cout<<"a = "<<a<<endl;//1000
return 0;
}
7.引用作为函数返回值
int getA1()
{
int a = 10;
return a;
}//value_a = a;
int &getA2()
{
int a = 20;
return a;
}//匿名引用 int &=a; ===> int &temp = &a;对a的地址取别名
int &getBB(){
static int b = 200;
return b;
}
int main(void)
{
int value_a = 0;
value_a = getA1();//正确,不是把a的空间返回来,而是把a的值拷贝回来
cout<<"value_a="<<value_a<<endl;//10
value_a = getA2();
//int value_a = temp; //int value_a = *temp 这是一个值拷贝
cout<<"value_a="<<value_a<<endl;//20
int &r_a = getA2();//没有值拷贝操作,禁止!
cout<<"r_a"<<r_a<<endl;//10
int &r_b = getBB();//此时的r_b就是getBB中的static int b
//当函数返回值,不是子函数局部变量,就可以用引用进行接收
cout<<"r_b"<<r_b<<endl;//没问题
r_b = 2000;//此时r_b就是getBB()中b的别名,修改r_b就是修改b
int value_b = getBB();//会有一个临时的引用int &temp = b;value_b = *temp;发生了值拷贝
cout<<"value_b"<<value_b<<endl;//2000
getBB() = 3000;//static b = 3000,当引用作为函数的返回值的时候,只要这个引用是合法的,就可以当左值
}
8.指针引用
struct Teacher
{
int id;
char name[64];
};
int getTeacher(Teacher **pp)
{
Teacher *p = NULL;
p = (Teacher *)malloc(sizeof(Teacher));
if(p==NULL)
{
cout<<"error"<<endl;
return -1;
}
memset(p,0,sizeof(Teacher));
p->id = 30;
strcpy(p->name,"zhang3");
*pp = p;
return 0;
}
int getTeacher02(Teacher * &tr){//此时tr就是main中的tp
//这里就直接对外面的tp进行操作了
//不需要像上面那样取二级指针间接操作
tr = (Teacher*)malloc(sizeof(Teacher));
if(tr==NULL)
{
cout<<"error"<<endl;
return -1;
}
tr->id = 40;
strcpy(tr->name,"li4");
}
void freeTeacher(Teacher **pp)
{
if(pp == NULL)return;
Teacher *p = *pp;
if(p!=NULL){
free(p);
*pp = NULL;
}
}
void freeTeacher02(Teacher *&tr)
{
if(tr != NULL){
free(tr);
tr = NULL;
}
}
int main(void)
{
Teacher *tp = NULL;
//getTeacher(&tp);
getTeacher02(tp);
cout<<tp->id<<","<<tp->name<<endl;
//freeTeacher(&tp);
freeTeacher(tp);
}
9.const 引用
const引用有较多使用。它可以防止对象的值被随意修改。因而具有一些特性。
- const对象的引用必须是const的,将普通引用绑定到const对象是不合法的。这个原因比较简单。既然对象是const的,表示不能被修改,引用当然也不能修改,必须使用const引用。实际上,
const int a = 1;
int &b = a;
这种写法是不合法的,编译不过
- const引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是const引用与普通引用最大的区别。
const int &a = 2; //合法
const int &b = a;//也是合法的
int x = 3;
const int &re1 = x;//x可以改变,re1不可以改变
cout<<"re1 = "<<re1<<endl;
x = 20;
cout<<"re1 = "<<re1<<" "<<"x = "<<x<<endl;//都是20
------so-------
//const引用一般用在形参上,来限制被引用的变量不能被修改
void printX(const int &re)//re不能被改变!
{
cout<<"re "<<re<<endl;
}
------------------------
const int &re2 = 10;
//用const引用一个字面量
//当用一个const引用去引用一个字面量的时候,字面量是没有地址的
//引用是无法对字面量取地址的,临时创建一个int temp , 10---->temp
//const int &re2 = temp;
//用re2就是代表temp,re2是const的引用,你无法去改变temp的值
int &re2 = 10;//这样会报错!非常量引用必须是左值,左值是可以赋值的,有内存空间的
cout<<"re2 "<<re2<<endl;
网友评论