引用 const 指针
(1) 定义引用时 必须初始化
(2) 没有所谓的空引用
(3) 没有引用的引用
int main()
{
int a = 10;
int& b = a;
b += 10;
a += 20;
int* p = &a;
int* s = &b;
cout<<p<<endl;
cout<<s<<endl;
//可以得到 p==s
//证明 a和b指的是同一个实体
}
const与指针
int a=10,b=20;
int *p1=&a;
*p1=100;
p1=b;
//const 修饰指针所指之物 限制不能修改指针所指之物的值
const int *p2=&a; int x=*p2; //可读
int const *p3=&a; *p2=100;//error 不可以修改指针指向的值
p2=&b;//可以修改指针自身的值
//const 修饰指针自身 限制不能修改指针自身的值
int *const p4=&a; int x=*p4;//可读
*p4=100; //允许修改指向的值
p4=&b;//error 不允许修改自身的值
//const 既修饰指针指向也修饰指针自身 限制既不能修改指针自身的值也不能修改指针所指之物的值
const int*const p5=&a; int x=*p5//可读、
*p5=100;//不允许修改指向的值
p5=&b;//不允许修改指值自身的值
int a=10,b=20;
const int *p=&a; //不允许修改所指之物的值
int *s0=p; //error 能力扩展了 此处依然可以通过*s0改变a的值从而改变*p的值 矛盾
const int *s1=p;
int *const s2=p;//error 能力扩展了 const 只能限制 s2自身的值 此处依然可以通过*s2改变a的值从而改变*p的值 矛盾
const int * const s3=p;
int a=10,b=20;
//不允许修改指针自身的值
int * const p=&a; int x=*p;
*p=100;
p=&b;//error
int *s0=p; int x=*s0
*s0=100;
s0=&b;//true s0指向的修改 并不会影响 p的指向
const int *s1=p; s1=&b//true s1的修改 并不会影响 p的值
int *const s2=p;
const int * const s3=p;
指针的引用
---------------------------------------------
int a=10,b=20;
int *s=&a;
int *&p1=s;//p1是s的别名
*p1=100; a的值改变 => *s=100
p1=&b <==> s=&b;
const int *&p2=s; *s+=100; a的值也会改变
*p2+=100;//error
s=&b; 导致 p2=&b <==> *p2=b//ok
int *const &p3=s; p3=&b;//error
s=&b; 导致 p3=&b;
*p3=100;//ok
const int *const &p4=s; p4=&b;//error
*p4=100;//error
s=&b; 导致 p4=&b
*s=100 a的值也会改变 *p4也会改变
---------------------------------------------
int a=10,b=20;
const int *s=&a; //*s=100; error
int *&p1=s; //error *p1的改变 会影响 *s的值
const int *&p2=s;
int *const &p3=s; //error *p1的改变 会影响 *s的值
const int *const &p4=s;
----------------------------------------------
int a=10,b=20;
int *const s=&a; //*s=100; error
int *&p1=s;
//error p1是s的别名 p1自身的改变会影响s的改变 const 对s自身改变进行了封锁
const int *&p2=s;
//error p3是s的别名 p3自身改变会影响s的改变 因为const 对s自身改变进行了封锁
int *const &p3=s;
const int *const &p4=s;
const 易错点
class Array
{
int* ptr;
public:
Array(int x)
{
ptr = new int[10];
for (int i = 0; i < 10; ++i)
{
ptr[i] = x;
}
}
//void fun(const Array * const this)
//此处第一个const 限制this指针所指之物不能改变
//this所指对象中 ptr的值不能改 this->ptr 不能改 也就是 ptr不能指向其他地址
//但是不能对ptr所指之物进行限制 此const没有连级性约束
//this->ptr[i]+=100; //true
//只有在设计Array时 设计为 const int *ptr;才可以约束 ptr的指向
void fun()const
{
for (int i = 0; i < 10; ++i)
{
this->ptr[i] += 100;//true
}
}
};
引用的本质
引用的本质是被限制的指针(限制指针自身的值不能改变)
int a=10;
int &b=a; ==> int * const b = &a;
引用必须初始化的原因
int a=10;
int &b; ==> int *const b; //error b的值是随机值 野指针
b=&a;//error const 限制了 b的值 不能被修改
所以: 指针自身是常性时必须初始化
int *const b=&a;//指针自身是常性必须初始化
所以: 引用必须初始化
int &b=a;
得出:引用必须初始化
常引用
常引用可以引用以下几种类型的数据:
1.普通变量
2.常变量
3.字面常量
int a = 10;
const int a1 = 100;
const int& b1 = a1; //常引用引用常变量
const int& b = a; //常引用引用普通变量
//const int * const b = &a;
const int& c = 10; //常引用可以引用字面常量
//等价下面三步
//int tmp = 10; //先定义一个临时量
//const int& c = tmp; //引用临时量
//const int* const c = &tmp;
汇编
函数返回值为引用的注意事项
首先看一个例子
int* fun() {
int ar[10] = { 12,23,34,45,56,67,78,89,90,100 };
return ar;//返回数组首地址 存入一个临时量之中
}
int main() {
int* p = fun(); //返回数组 ar的首元素的地址
//调用函数 分配栈帧
//函数调用结束 分配的栈帧销毁
//此时 p指向 ar首元素的地址 但是由于 函数分配的栈帧已被销毁 p此时是失效指针
//不能从被回收的空间中获取值(错误的 不安全的)
for (int i = 0; i < 10; ++i) {
printf("%p => %d\n", p, *p);
//printf函数也会分配栈帧
//会骚扰到之前 fun使用的且当前已经回收的栈帧
p += 1;
}
return 0;
}
/* 输出结果:
00AFF960 => 12
00AFF964 => -858993460
00AFF968 => -858993460
00AFF96C => -858993460
00AFF970 => 0
00AFF974 => -858993460
00AFF978 => -858993460
00AFF97C => 23
00AFF980 => 4659967
00AFF984 => 11532924
*/
//结论:不可以从失效空间中获取值 这是不安全的
以引用返回局部变量 (不允许 不安全)
函数能把什么样的数据以引用返回呢?
此变量的生存期不受函数生存期的影响(函数的生存期不会影响变量的生存期),那么此变量可以以引用返回.
函数结束,变量仍然存活就可以以引用方式返回.
以下几种类型的变量 函数可以以引用返回
1.加上静态关键字 static 的变量(静态变量)(静态变量在数据区,函数结束,静态变量依然存活).
2.全局变量
3.以引用进入的变量(形参为引用,形参可以以引用返回)
4.堆区
练习例子
class Object
{
private:
int value;
public:
Object(int x = 0) :value(x) {}
~Object() {}
void SetValue(int x) { value = x; }
int GetValue() { return value; }
//使用一个函数实现SetValue和GetValue()函数的功能
//既能设置值也能获取值
//int * const Value(Object * const this)
int& Value() { //普通对象可以调用
return value;
}
//const int * const Value(const Object * const this)
const int& Value() const //常对象可以调用,普通对象也可以调用
{//返回值类型必须与函数类型相匹配 形参为const int * const 如果要返回形参的值 返回值类型也必须为 const int * const
return value;
}
};
int main() {
Object obj(10);//普通对象
obj.Value(); ·
//首先调用最为匹配的 int& Value();
//如果没有最匹配的函数 可以退而求其次 调用重载函数const int& Value()const
const Object objc(20);//常对象
objc.Value();
//只能调用 const int& Value()const
int x = 0;
x = obj.Value(); //x=obj.value;
obj.Value() = 100; //obj.value=100;
cout << x << endl;
}
左值和右值 右值引用
左值右值定义
//lvalue 左值 xvalue rvalue 右值 prvalue 重右值
//左值可以取地址&; 生存期和变量名一样
//右值 不可以取地址 因为无名字
//字面常量 比如:10 叫做重右值
int main() {
//可以取地址的值就是左值
int a = 10; //左值
//左值可以取地址&a; 生存期和变量名一样
//右值 不可以取地址 无名字
int& b = a;
int&& c=10;
//int&& d = c; error c虽然是10的右值引用 但是 此处c是个变量名已经具有名字 一旦具有名字就不是右值 就变成一个左值了
//具有名字 就是左值
String s1;
String& sx=s1; //正确
//String& sx=String("hello");// error 调用构造函数构造的是一个无名对象 不具有名字; 左值引用必须引用一个具有名字的对象
String&& sy=String("hello");// true 调用构造函数构造的是一个无名对象 可以使用右值引用
}
右值引用
int main() {
const int& a = 10;
//相当于
//int tmp=10; 1.定义临时空间,存放10
//const int& a = tmp;
//const int* const = &tmp; 2.用指针a指向临时空间
int&& b = 10; //右值引用
//b = 10;
// 底层
//int tmp = 10;
//int* const b = &tmp; 可以改变值
return 0;
}
int fun()
{
int a = 10;
return a;
}
int main()
{
int x = fun();
//将亡值 在函数调用结束 将亡值消失
//int& b = fun(); //error fun() 此时 是10 是一个右值
const int& c = fun();
//int tmp = 10;
// const int * const c = &tmp;
//直接把a的值放入主函数栈帧中
//c引用此空间
//值不可改变
int&& d = fun();
//int tmp = 10
// int * const d = &tmp;
//直接把a的值放入到主函数栈帧空间中
// b引用
//值可以改变
//相当于把 将亡值的生存期 扩大到 与 右值引用具有相同的生存期
}
、