1.常量指针与指针常量的区分
A、常量指针(const int* p)
- 常量指针本质上是一个指针,是一个指向“常量”的指针,即不能通过指针改变指向对象的值(不能解除引用),但可以更改指向。用法如下
int a=3,b=4;
const int *p=&a; /初始化指向a
p=&b; /可以更改指向,现在指向b
*p=5; /不可以通过指针改变对象的值
B、指针常量(int* const p)
- 指针常量本质上是一个常量,const是修饰p的,即指针的值自身是一个常量,不可改变,始终指向一个地址,在创建的同时必须进行初始化,用法如下
int a=3,b=4;
int* const p=&a; /初始化指向a
*p=5; /可以进行解引用,更改指向对象的值
p=&b; /不可以更改指向
C、用例区分
int a=3,b=4;
const int c=5; /常量,创建时就需要初始化
const int* p1; /常量指针
int* const p2=&a; /指针常量,创建时就需要初始化,指向a
-----------------------------------------------------------
int *wrong=&c; /错误,常量地址不能初始化普通指针,只能赋值给常量指针
int* const p=&c; /错误,报错:无法从“const int *”转换为“int *”,即必须拥有相同的底层const资格
p1=&c; /正确,只能赋值给常量指针
-----------------------------------------------------------
const int* const p3=&a; /指向“常量”的指针常量,具有常量指针和指针常量的特点,指针内容不能改变,也不能指向其他地方,定义同时要进行初始化
*p3=5; /错误
p3=&b; /错误
--------------------------------------------------------------------
char* p4="abc"; /"abc"是字符常量放在常量区,p4指向的是"abc"的首地址,即'a'的地址
*p4='b'; /错误,*p4相当于对字符'a'修改,但"abc"是一个字符串常量,不可修改
D、总结
- 常量指针与指针常量,只要看最后的两个字,是指针还是常量。若是常量,则创建时就必须进行初始化
2.常量引用与引用常量区分
A、常量引用(const int& a)
- 本质上是一个引用,对常量的引用,不能通过引用改变绑定对象的值
注:当初始化值是一个字面值时,则只能对一个const T&(常量引用)赋值。而且这个赋值是有一个过程的:首先将值隐式转换到类型T,然后将这个转换结果存放在一个临时对象里,最后用这个临时对象来初始化这个引用变量。
int a=3;
const int& reference1=a;
const int& reference2=5;/ 对常量、字面值的引用只能通过常量引用,会创建一个临时对象,用这个对象进行初始化
reference1=5; /错误,常量引用不能改变对象的值
注特殊用法:当函数返回一个局部变量的时候,返回局部变量的引用是不对的(报错:返回局部变量或临时变量的地址),但是用这个局部变量去初始化一个常量引用,那这个临时变量的生命周期就会被延长,直到引用被销毁
这个特性有时很有用,比如,你可以用一个基类的引用指向一个子类的临时变量,然后通过这个引用来实现多态,但又不用处理子类的销毁,如下
Class Base()
{
public:
virtual Bar() { cout << "base bar()" << endl; }
};
Class DerOne: public Base
{
public:
virtual Bar() { cout << "DerOne Bar()" << endl; }
};
class DerTwo: public Base
{
public:
virtual Bar() { cout << "DerTwo Bar()" << endl; }
};
Base GetBase()
{
return Base();//返回一个局部对象
}
DerOne GetDerOne()
{
return DerOne();//返回一个局部对象
}
DerTwo GetDerTwo()
{
return DerTwo();//返回一个局部对象
}
int main()
{
const Base& ref1 = GetBase();//用局部对象去初始化常量引用
const Base& ref2 = GetDerOne();//用局部对象去初始化常量引用
const Base& ref3 = GetDerTwo();//用局部对象去初始化常量引用
ref1.Bar();//实现多态
ref2.Bar();//实现多态
ref3.Bar();//实现多态
return 0;
}
在这个情况下,局部对象不会被直接析构,而是会保留到引用的生命周期结束为止。这样使得在一个域内使用多态时,可以避免使用指针
再另外提一下,何时会产生局部对象/临时变量/局部变量?
Base GetBase()
{
return Base();//返回一个局部对象
}
上述这样会产生一个临时变量。但并不是所有返回值都会创建临时变量,只有当没有将返回值初始化其他变量时,临时变量才会创建. 如果main函数中调用如下
Base ref1=GetBase(); /将返回值作为ref1的初始化值,直接调用拷贝构造函数,不生成临时变量
B、引用常量(int& const t???)
- 注意:原则上不存在引用常量(所以我使用了???),会报warning C4227: 使用了记时错误: 忽略引用上的限定符。为什么?
- 原因:在上一篇文章C++ 指针与引用的区别中写道:从汇编的角度来看“引用”,编译器把他解释为指针常量,既然已经是常量,再加const不是多此一举吗,所以加不加都一样