C++ 辨析常量指针、指针常量与常量引用、引用常量

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不是多此一举吗,所以加不加都一样

C、总结

常量引用语法上可以引用一个临时变量。这种方法在使用引用作函数参数和返回局部变量时有意义。但更多时候,常量引用主要用在作函数参数或保证不修改原变量的时候。

  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值