【C++基础】常量引用、指针常量与常量指针 ; 顶层const与底层const

本文详细阐述了C++中常量引用和指针常量的概念与应用。常量引用用于确保函数不修改对象,同时保持引用的效率优势;指针常量则分为不能修改对象的指针常量和不能改变指向的常量指针。通过实例解析了它们在参数传递中的差异,强调了顶层const和底层const的区别。
摘要由CSDN通过智能技术生成

一、常量引用

我们用引用作形参往往是为了能修改对象的值,但有时候又用“对const的引用”,如const int& a,作为形参,这样就使得该对象不能被函数修改了,那这样用引用的意义不就没有了,和传值(如int a)作形参不就一样了?

事实上,一方面引用避免了拷贝,对于占用内存大的对象来说,传引用依然比传值会更有效率,所以传引用作形参还是有意义的。另一方面,如果我们不希望被引用绑定的对象被该函数修改,所以我们就会采用“对常量的引用”,简称常量引用,如const int& r = i;
这样引用及其对应的对象都是常量,因为首先C++不允许随意改变引用所绑定的对象,所以引用本身是一种常量,而经过const修饰后的变量i也成为了常量(这指的是不能通过引用去修改它)。

而且,使用引用而非常量引用也会极大地限制函数所能接受的实参类型,比如我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。
C++ primer 5th第六章有这样一个例子

string::size_type find_char(string &s, char c, string::size_type &occurs){
//具体实现不写
//第一个形参为string &s
}//string::size_type是size()函数的返回类型,size()返回string对象的长度,见C++ primer 5th P79.

可见,第一个形参是普通引用,而非常量引用。那么在调用函数时,如果遇到“hello word”这种const型的实参(它默认是const char*型),就会报错,如

find_char("hello world", 'a', ctr);

而将第一个形参改成const string &s则不会报错。

补充:
前面说过,常量引用仅仅是说不能通过引用去修改该对象,对象本身是非常量的话,也可以通过其它途径改变,如下

int i = 42;//i没有被声明为const int i = 42,所以可以改变
int &r1 = i;//引用r1绑定对象i,且不是常量引用
const int &r2 = i;//r2也绑定对象i,但不允许通过r2修改i的值
r1 = 0;//正确
r2 = 0;//错误

二、指针常量与常量指针

(1)指针常量
指针常量是“指向常量的指针”的简称,英文是 pointer to const。顾名思义,指针常量不能用于改变其所指对象的值,并且想存放常量对象的地址,只能使用指向常量的指针,即指针常量。
写作:const int *p 或 int const *p
和常量引用一样,指向常量的指针也没有规定其所指的对象必须是一个常量。所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变。

(2)常量指针
常量指针(const pointer)是该指针不能改变指向,即地址值不能变。常量指针必须初始化,而且一旦初始化完成,它的值就不能再改变了。写作:int *const p,从形式上就能看出const修饰的是p,即指针。不过常量指针所指向的变量的值倒是可以变。

int errNumb = 0;
int *const curErr = &errNum;//curErr将一直指向errNumb

const double pi = 3.14;//变量pi是常量
const double *const pip = π//pip左边有个const限定,所以也是一个常量指针,外面还有一个const是因为他指向的是pi是个常量

*pip = 4.12;//错误,因为pip指向的pi是常量
*curErr = 2;//正确,因为errNumb不是常量
curErr = pip;//错误,因为curErr是常量指针

上面的const double *const pip = &pi中有两个const,我们把靠近指针pip的叫作顶层const (top-levep const),它表示指针本身是个常量,而最左边的叫作底层const (low-level const)表示指针所指的对象是个常量。
当然,顶层const和底层const具有一般性的应用,顶层const可以表示任意的对象是常量,这一点对任何数据类型都有用,如算数类型、类、指针等。底层const则与指针和引用等复合类型的基本类型部分有关。不过指针类型既可以是顶层,也可以是底层const,这一点和其它类型区别明显。

int i = 0;
const int ci = 42;//对ci来说,这是个顶层const,它使得ci的值不可变
const int *p = &ci;//对p来说,这是个底层const,p可以变,只是不能通过p去修改它指向的值
int &r = ci;//错误,普通的int型引用不能绑定到int常量上
const int &r2 = i;// 正确,即const型的引用可以绑定到普通的int上

因为 非常量 可以转成 常量,反之则不行
如非常量i可以被常量r2引用,而常量ci不能赋值给常量r,否则就可以通过引用r去修改ci,但ci是常量。不可被修改。而r2不能修改i,这也没事,i可以通过其他方式修改,这与第一部分是自洽的。
这也是为什么在以const修饰的引用作为形参的函数如fun(const int& a)中,可以将 非常量 (如int b = 0;fun(b);)作为实参去调用该函数。但是反过来就不行,就像第一部分例子中的“hello world”不能作为find_char的实参。

本文大多参考自C++ primer 5th
对应中文版页数为:p54 - p58 、p79 、p190 - p192

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值