通俗来讲:c++中的引用就是给变量起了一个别名,使同一块内存地址
拥有两个或多个不同的名字;通过引用修改变量的值会直接对内存中数
据进行操作(这和c中指针有几分相似),举个例子:
#include <iostream>
using namespace std;
void fun1(int &a)//c++引用
{
a = 1;
}
{
a = 1;
}
void fun2(int *a) //c指针
{
*a = 10;
}
{
*a = 10;
}
void fun3(int a)//传入的a相当于形参,对实参a无影响
{
a = 100;
}
int main()
{
{
a = 100;
}
int main()
{
int a;
fun1(a);
cout<<"a = "<<a<<endl;
fun1(a);
cout<<"a = "<<a<<endl;
fun2(&a);
cout<<"a = "<<a<<endl;
cout<<"a = "<<a<<endl;
fun3(a);
cout<<"a = "<<a<<endl;
return 0;
}
cout<<"a = "<<a<<endl;
return 0;
}
那么问题来了,既然引用是内存地址的别名,理所当然它不需要开辟新的
内存空间,引用就相当于它指向的内存空间本身。
我们用事实说话:
#include <iostream>
using namespace std;
struct Teacher
{
char name[32];
int age;
int &a;//很像指针,占内存空间
int &b;
};
{
char name[32];
int age;
int &a;//很像指针,占内存空间
int &b;
};
int main()
{
cout << "sizeof(Teacher):"<<sizeof(Teacher)<< endl;
{
cout << "sizeof(Teacher):"<<sizeof(Teacher)<< endl;
return 0;
}
}
(懒得贴图片我就手打出结果了,可以自己跑下)
输出的结果是:44
显然这推翻了我之前的想法,引用更像是一个指针,确切地说是一个指向
对象不可修改的指针(xx *const p)
接下来探究下引用的本质:
#include <iostream>
using namespace std;
struct Teacher
{
char name[32];
int age;
int &a;//很像指针,占内存空间
int &b;
};
//引用本质
void fun1(int &a1)
{
a1 = 100;
}
void fun2(int * const a1)
{
*a1 = 10;
}
int main()
{
int a = 10;
cout << "sizeof(Teacher):"<<sizeof(Teacher)<< endl;
{
char name[32];
int age;
int &a;//很像指针,占内存空间
int &b;
};
//引用本质
void fun1(int &a1)
{
a1 = 100;
}
void fun2(int * const a1)
{
*a1 = 10;
}
int main()
{
int a = 10;
cout << "sizeof(Teacher):"<<sizeof(Teacher)<< endl;
fun1(a); //指向函数调用,不需要取地址
cout << "a:"<<a<< endl;
cout << "a:"<<a<< endl;
fun2(&a); //传入指针需要手动取实参地址
cout << "a:"<<a<< endl;
return 0;
}
cout << "a:"<<a<< endl;
return 0;
}
fun1()与fun2()的功能是完全一致的,那么我们是不是就可以简单的认为引用
其实就是一个指针,只不过C++编译器自动地给我我们取了地址而已。
接下来深挖一下引用用法:
#include <iostream>
using namespace std;
int getA1()
{
int a;
a = 10;
return a;
}
//返回a 本身,返回a的一个副本10
int& getA2()
{
int a;
a = 10;
return a;
}
int getA1()
{
int a;
a = 10;
return a;
}
//返回a 本身,返回a的一个副本10
int& getA2()
{
int a;
a = 10;
return a;
}
int* getA3()
{
int a;
a = 10;
return &a;
}
int main()
{
int a1 = 0;
int a2 = 0;
a1 = getA1();
a2 = getA2(); //10
{
int a;
a = 10;
return &a;
}
int main()
{
int a1 = 0;
int a2 = 0;
a1 = getA1();
a2 = getA2(); //10
int &a3 = getA2(); //若返回栈变量,不能成为其他引用的初始值
cout << "a1:" <<a1<<endl;
cout << "a2:" <<a2<<endl;
cout << "a3:" <<a3<<endl;
return 0;
}
cout << "a1:" <<a1<<endl;
cout << "a2:" <<a2<<endl;
cout << "a3:" <<a3<<endl;
return 0;
}
这个程序跑出来:
a1 = 10 a2 =10 a3 = 4783468(乱码)
为何a2 能够正常返回而a3却不能呢?
实际上,函数的返回值是一个引用,就看你用什么东西来接收它。
对应上面程序:
a2是一个变量,a2 = getA2()接受的是a的一个副本,虽然函数被析构了,
但析构之前会把返回值放在寄存器里面,当用a2接收是从寄存器中弹出;
对于a3却不是那么回事,a3是一个引用,相当于一个常量指针,他所指向
的是getA2()的地址,函数一旦被析构,函数中的值也会被抹去,但地址还在,
所以返回的结果是a3得到一串乱码
故在使用引用时要注意:当函数返回值为引用时,若返回栈变量,其不能成为其他引用的初始值,而且不能作为左值使用。
再看一个例子:
#include <iostream>
using namespace std;
int j1()
{
static int a = 10;
a++;
return a;
}
{
static int a = 10;
a++;
return a;
}
int& j2()
{
static int a = 10;
a++;
return a;
}
int main()
{
int a1 = 10;
int a2 = 20;
{
static int a = 10;
a++;
return a;
}
int main()
{
int a1 = 10;
int a2 = 20;
a1 = j1();
a2 = j2();
int &a3 = j2();
cout << "a1:" <<a1<<endl;
cout << "a2:" <<a2<<endl;
cout << "a3:" <<a3<<endl;
return 0;
}
a2 = j2();
int &a3 = j2();
cout << "a1:" <<a1<<endl;
cout << "a2:" <<a2<<endl;
cout << "a3:" <<a3<<endl;
return 0;
}
这次采用了静态变量,输出结果为 : a1 = 11 a2 = 11 a3 = 12
故:若返回静态变量或者全局变量,可以成为其他引用的初始值,即可作为右值使用,也可作为左值使用
下面看看引用当左值的情况:
#include <iostream>
using namespace std;
//返回变量的值
int g1()
{
static int a = 10;
a++;
return a;
}
//返回变量本身,返回变量标识的内存空间
int& g2()
{
static int a = 10;
a++;
return a;
}
int main()
{
// g1() = 100; //这相当于 11 = 100
//cout << "g1 = " << g1() <<endl;
int g1()
{
static int a = 10;
a++;
return a;
}
//返回变量本身,返回变量标识的内存空间
int& g2()
{
static int a = 10;
a++;
return a;
}
int main()
{
// g1() = 100; //这相当于 11 = 100
//cout << "g1 = " << g1() <<endl;
g2() = 100; //这相当于 a = 100
cout << "g2 = " << g2() <<endl;
cout << "g2 = " << g2() <<endl;
return 0;
}
}
引用返回的是一个变量标识的空间,也就决定了它可以被赋值(即当左值使用)
故:若函数当左值,必须返回的是一个引用
指针的引用:
#include <iostream>
#include<malloc.h>
using namespace std;
using namespace std;
struct Teacher
{
char name[32];
int age;
};
{
char name[32];
int age;
};
//在被调用函数,获取资源
int getTeacher01(Teacher **p) //二级指针
{
Teacher *tmp = NULL;
if(p == NULL)
{
return -1;
}
tmp = (Teacher *)malloc(sizeof(Teacher));
if(tmp == NULL)
{
return -2;
}
tmp->age = 33;
int getTeacher01(Teacher **p) //二级指针
{
Teacher *tmp = NULL;
if(p == NULL)
{
return -1;
}
tmp = (Teacher *)malloc(sizeof(Teacher));
if(tmp == NULL)
{
return -2;
}
tmp->age = 33;
//P是实参的地址, *实参的地址去间接的修改实参的值
*p = tmp;
}
*p = tmp;
}
//指针的引用做函数参数
int getTeacher02(Teacher* &myp)
{
//给myp赋值相当于给函数中的PT1赋值
myp = (Teacher*)malloc(sizeof(Teacher));
if(myp == NULL)
{
return -1;
}
myp->age = 36;
int getTeacher02(Teacher* &myp)
{
//给myp赋值相当于给函数中的PT1赋值
myp = (Teacher*)malloc(sizeof(Teacher));
if(myp == NULL)
{
return -1;
}
myp->age = 36;
}
void FreeTeacher(Teacher *PT1)
{
if(PT1 == NULL)
{
return ;
}
free(PT1);
}
void FreeTeacher(Teacher *PT1)
{
if(PT1 == NULL)
{
return ;
}
free(PT1);
}
int main()
{
Teacher *PT1 = NULL;
{
Teacher *PT1 = NULL;
//c语言中的二级指针
getTeacher01(&PT1);
cout << "age:" <<PT1->age <<endl;
FreeTeacher(PT1);
getTeacher01(&PT1);
cout << "age:" <<PT1->age <<endl;
FreeTeacher(PT1);
//c++中的引用(指针的引用)
getTeacher02(PT1);
cout << "age:" <<PT1->age <<endl;
FreeTeacher(PT1);
return 0;
}
getTeacher02(PT1);
cout << "age:" <<PT1->age <<endl;
FreeTeacher(PT1);
return 0;
}
常引用:
#include <iostream>
using namespace std;
int main()
{
int x = 10;
{
int x = 10;
const int &m1 = x; //用变量初始化常引用,引用只有只读属性
x = 20; //可以对x进行修改
//m1 = 30; //报错,不能通过引用m1修改x的值
cout << "m1:" << m1 <<endl;
//m1 = 30; //报错,不能通过引用m1修改x的值
cout << "m1:" << m1 <<endl;
const int &m2 = 40; //用字面量初始化常引用,系统会给m2分配内存空间
//int &m3 = 50; //普通引用,这样写会报错,m3相当于一个指针,可是50却是字面量没有内存
cout << "m2:" << m2 <<endl;
return 0;
}
//int &m3 = 50; //普通引用,这样写会报错,m3相当于一个指针,可是50却是字面量没有内存
cout << "m2:" << m2 <<endl;
return 0;
}