辨析对常量的引用、指向常量的指针、常量指针

1.关键字const我们常用关键字const对变量的类型加以限定,表示此变量的值不能被改变,而且正因为const对象一旦创建后其值就不能被改变,所以const对象必须初始化,其初始值可以使任意复杂的表达式。在初始化中,利用一个对象去初始化另一个对象,他们是不是const并无关系,例如:

<span style="font-family:Times New Roman;font-size:18px;">int i=42;
const int ci=i;
int j=ci;</span>

2.对常量的引用:我们可以把引用绑定到常量或者非常量对象上,与普通引用不同的是,这种引用不能通过引用本身来修改它所绑定的对象,我们称之为对常量的引用,注意,这里面对常量的引用并不是指引用的对象一定是常量,再次啰嗦一遍,他只是指不能通过引用本身来修改与它所绑定的对象,也就是说,常量引用只是对引用可参与的操作进行了限定,对于引用本身是不是一个常量未做限定,因为对象也可能是一个非常量,所以允许其他途径改变他的值。但是,如果一个变量为常量,要想对其引用的话则必须是对常量的引用,普通引用会有错误。有时候我们简称它为”常量引用“,这个简称在意思上来说很到位,但是要清楚的是,引用并不是对象,它没有属于自己的内存,所以没法让引用保持不变,它依附于所绑定的对象(参考引用的定义),

<span style="font-size:18px;"><span style="font-family:Times New Roman;">int ci=1024;
const int &r1=ci;
r1=42;          </span>    <span style="font-family:Times New Roman;">                   //错误:r1是对常量的引用

const int r2=100;
int &r2=ci;                          //错误:试图让一个非常量引用指向一个常量对象</span>

<span style="font-family:Times New Roman;">//因为ci是一个常量,不允许直接为其赋值,所以也不能通过r2去改变ci的值,因此对于一个常量,其引用必须是对常量的引用才行/,普通引用是错误的。</span>

<span style="font-family:Times New Roman;">int i =42;
int &r1=i;
const int &r2=i;
r1=0;                              //正确,r1并非常量
r2=0;  </span>                 <span style="font-family:Times New Roman;">//错误,r2是对常量的引用</span></span>

 
      我们知道,除两种特殊情况外,所有引用的类型都要和与之绑定的对象严格匹配,而且,引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起,这是什么原因呢,我们来看看一个例子: 
<span style="font-family:Times New Roman;font-size:18px;">double dval=3.14;
int &ri=dval;
//上面代码中ri引用了一个int 型的整数,对ri的操作应该是整数运算,但dval却是一个双精度浮点数而非整数,因此为了确保让ri 绑定一个整数,编译器把上述代码变成了如下形式:
int temp=dval;
int &ri=temp;
//在这种情况下,ri绑定的是一个临时量,程序员既然想让ri绑定dval,肯定就是想通过ri改变dval的值,否则干什么要给///ri赋值呢,///而实际上ri只能改变temp的值,所以C++将这种行为定为了非法。</span>


       刚刚我们说了有特殊情况,其中之一就是初始化常量引用的时候允许任意表达式作为初始值,只要该表达式能转换成引用的类型即可,尤其是,允许为一个常量引用绑定非常量的对象、字面值、甚至是一个表达式。上面的例子中,如果ri是对常量的引用,我们看发生了什么:

<span style="font-family:Times New Roman;font-size:18px;">double dval=3.14;
const int &ri=dval;

const int temp=3.14;
const int &ri=temp;
//此处ri是对常量的引用,编译器把代码变成如下形式,ri是临时量temp的引用,又因为是常量引用,本身就不能通过引用去改变dval///的值,所以他是合法的。</span>


 

3.指向常量的指针:与引用一样,也可以令指针指向常量或非常量。类似于常量引用,指向常量的指针不能用于改变其所指对象的值,要想存放常量对象的地址,只能使用指向常量的指针。

       和引用类似,所有指针的类型都要和与之指向的对象严格匹配,特殊情况是指向常量的指针可以不匹配

<span style="font-family:Times New Roman;font-size:18px;">const double pi=3.14;
double *ptr=&pi                    //pi是个常量,必须要指向常量的指针指向它
const double *cptr=&pi
*cptr=42;                              //错误,不能够给*cptr赋值
double dval=3.14;
cptr=&dval;                          //正确,允许另一个指向常量的指针指向一个非常量</span>


4.const指针:指针是对象而引用不是,因此就像其他对象类型一样,允许把指针本身定为常量。常量指针必须初始化,而且一旦初始化完成,它的值(也就是存放在指针中的那个地址)就不能再改变了,把*放在const关键字之前说明指针是一个常量。需要注意的是,指针本身是常量并不意味着不能通过指针修改其所指向的对象的值,能否这样做完全依赖于对象的类型。

<span style="font-family:Times New Roman;font-size:18px;">int errNumb=0;
int *const curErr=&errNumb;       //curErr是一个常量指针
const double pi=3.14159;
const double *const pip=π            //pip是一个指向常量对象的常量指针</span>


5.顶层、底层const:我们知道,指针本身是一个对象,它又可以指向另外一个对象,因此,指针本身是不是一个常量以及指针所指的是不是一个常量就是两个相互独立的问题,用名词顶层const表示指针本身是个常量,而用名词底层const表示指针所指的对象是一个常量。更一般的,顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用,如算术类型、类、指针等。底层const则与指针和引用等符合类型的基本类型部分有关,比较特殊的是,指针类型既可以是顶层const也可以是底层const。

<span style="font-family:Times New Roman;font-size:18px;">int i=0;
int *const p1=&1;               //不能改变P1的值,这是一个顶层const
const int ci=42;                   //不能改变ci的值,这是一个顶层const
const int *p2=&ci;              //可以改变p2的值,只是一个顶层const
const int *const p3=p2;      //靠右的const是顶层const,靠左的是底层const
const int &r=ci;                  //用于申明引用的都是底层const

i=ci;                                    //ci是一个顶层const,对此操作无影响
p2=p3;                                //p3是顶层const,对此无影响


int *p=p3;                          //错误:p3包含底层const的定义,而p没有
p2=p3;                               //p3和p3都是底层const
p2=&i                                //int *能转换成const int *
int &r=ci                            //错误:普通的int 引用不能绑定到int 常量上
const int &r2=i                  //const int &可以绑定到一个普通int上</span>

另外,当执行对象的拷贝操作时,常量时顶层const还是底层const区别明显,其中,顶层const不受影响,而底层const却受限制,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之则不行:

6.constexpr(C++11新标准)和常量表达式:常量表达式是指值不会改表并且在编译过程就能得到计算结果的表达式,显然,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式,也就是说,判断一个表达式是否是常量表达式有两点:一是看数据类型是否是const类型,二是看初始化的值是否是常量表达式。

<span style="font-family:Times New Roman;font-size:18px;">const int max_sfiles=20;               //是常量表达式
const int limit=max_files+1;        //是常量表达式
int staff_size=27;                         //不是常量表达式,数据类型并非const类型
const int sz=get_size();                //不是常量表达式,初始值并非常量表达式</span>

constexpr:C++11新标准规定,允许将变量 声明 为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式,声明为 constexpr的变量一定是一个常量,而且必须用常量表达式初始化。常量表达式的值需要在编译时就得倒计算,因此对声明constexpr时用到的类型须有所限制。他们的值比较简单,也容易得到,把它们称为“字面值类型”,算术类型、引用和指针都属于字面值类型。尽管指针和引用都能定义成constexpr,但他们的初始值受到了严格限制,一个constexpr指针的初始值必须是nullptr或者0,或者是存储在某个固定地址中的对象。另外需要注意的是,由constexpr声明的指针,其对象将被置为顶层const,类似于指针常量。

<span style="font-family:Times New Roman;font-size:18px;">const int *p=nullptr;             //p是一个指向整型常量的指针(底层const)
constexpr int *q=nullptr;       //q是一个指向整数的常量指针(顶层const)</span>


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值