const限定符

    const是c++的关键字之一,被const修饰的都受到强制保护,可以预防意外的变动,提高程序的健壮性。

    我把const的使用情况分为四种:const对象(变量) ,const引用,const和指针,const和函数,下边依次介绍。

 

     1.   const对象


      我们经常遇见的使用const修饰变量的目的是为了防止变量的值被修改:

      例如,定义一个变量bufsize代表一个常数512的时候会有一个严重的问题:即bufsize是可以被修改的,它可能被有意无意的修改。const限定符提供了一个解决方法:它把一个对象转换成一个常量。

       

      定义bufsize为常量并初始化为512(因为常量在定义后就不能被修改,所以定义时必须初始化)。

      变量bufsize仍然是一个左值,但现在这个左值是不可修改的。任何修改bufsize的尝试都会导致编译错误:

      注意:用常量表达式初始化的const变量,大部分编译器在编译是会用相应的常量表达式替换这些const变量的任何使用,所以在实践中不会有任何存储空间用于存储用常量表达式初始化的const变量,当使用const_cast去掉变量的const属性后,再对变量赋值,会发生如下情况:

     对同一块内存地址有两个不同的值,这是因为const变量t是用常量1来初始化的,在访问t时,直接用内存中的1替换了。如果不用常量表达式来对t进行初始化,就正常了:

 

      另外一种使用const对象的方法跟变量的作用域有关:

      在全局作用域里定义非const变量时,它在整个程序中都可以访问。我们可以把一个非const变量定义在一个文件中,假设已经做了合适的声明,就可以在另外的文件中使用这个变量:

      //file_1.cpp

      int counter;         //definition

      //file_2.cpp

      extern int counter;     //uses counter from file_1

      ++counter;               //increments counter defined in file_1

 

      与其他变量不同,除非特别说明,在全局作用域声明的const变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。

      通过指定const变量为extern,就可以在整个程序中访问const对象:

      //file_1.cpp

     extern const int counter = 0;         //defines and initializes

      //file_2.cpp

      extern const int counter;     //uses counter from file_1

      cout<<counter;               //counter = 0

 

      有时会声明一个类对象为const对象。

      如果将某个类对象声明为const,则编译器不允许该对象调用任何可能修改它的成员函数。

      所以在使用这个const对象的接口的时候,要保证不会修改const对象的值,因此需要把调用的接口函数修饰为const,在函数头后面加const修饰即可,这就是const函数的用法,仅当一个函数是类成员时,修饰为const才有意义。

      如果想要修改成员变量,需要在变量前加mutable 修饰。

 

      2.   const引用

 

      引用是对象的另一个名字,const引用是指向const对象的引用:

         可以读取但是不能修改refbuf,因此,任何对refbuf的赋值都是不合法的,这个限制的意义是:因为不能直接对bufsize赋值,所以不能通过使用refbuf来修改bufsize。

       非const引用可以修改其所指向的对象的值,正如ref2,所以将普通的非const引用绑定到const对象上是不合法的。

 

       const引用可以初始化为不同类型的对象 或者 初始化为右值,如字面常量:

     

       同样的初始化对于非const引用却是不合法的,而且会导致编译时错误。其原因非常微妙啊~

       观察将引用绑定到不同的类型时所发生的事情:

       double dval = 3.14;

       const int &ri = dval;

       编译器会把这些代码转换成如下形式的编码:

       int tmp = dval;

       const int &ri = dval;

       如果ri不是const,那么可以给ri赋一个新的值,这样做不会修改dval,而是修改了tmp。期望对ri的赋值会修改dval的程序员会发现dval并没有被修改。仅允许const引用绑定到需要临时使用的值完全避免了这个问题,因为const引用是只读的。

       非const引用只能绑定到与该引用同类型的对象。

       const引用则可以绑定到不同但是相关的类型的对象或者绑定到右值。

 

     3.   const和指针

       指针和const限定符之间有两种交互:即指向const对象的指针和const指针。另外还有指向const对象的const指针。

       1. 指向const对象的指针

       可以使用指针来修改它所指向的对象的值,如果指针指向const对象,则不允许用指针来修改其所指的const值。为了保持这个特性,c++强制要求指向const对象的指针也必须具有const特性:

       const double *cptr;    //cptr may point to a double that is const

       这里的cptr是一个指向double类型const对象的指针,const限定了cptr指针所指向的对象的类型,而并非cptr本身,即cptr本身不是const,在定义时不需要对它进行初始化。如果需要的话,允许给cptr重新赋值,使其指向另一个const对象,但是不能通过cptr修改其所指对象的值:

 

        把一个const对象的地址赋给一个非const对象的指针也会导致编译时错误:

 

        另外,不能使用void*指针保存const对象的地址,而必须使用const void* 类型的指针保存const对象的地址:

 

        允许把非const对象的地址赋给指向const对象的指针,但不能通过指针修改其值,因为指向const对象的指针一经定义就不允许修改其所指向的对象的值:

         可以看到,在使用指向const对象的指针时,如果它指向一个非const对象,这个const对象可以通过其他方法改变它的值,导致const指针指向的对象的值也变化了,所以,不能保证指向const的指针所指对象的值一定不可修改。

 

         2.  const指针

         const指针本身的值不能修改:

         int err = 0;

         int *const p = &err;

         const指针p的值不能修改,即不能使p指向其他对象,p需要在定义时初始化。

         const指针所指的对象的值是否能修改取决于该对象的类型,即可以使用const指针修改其所指向的对象的值。

        3.  指向const对象的const指针

        

        既不允许修改pp所指向对象的值,也不允许修改pp的指向。

 

        4.   typedef和指针

        typedef string *pstring;

        const pstring cstr;

 

        cstr的实际类型是: string *const cstr;

 

 

        4.   const和函数

        当一个函数是类的成员函数时,修饰为const才有意义。

        对于函数的形参,如果是指针形参:

        指针形参的类型将影响函数调用所使用的实参:可以将指向const型和非const型对象的指针传递给指向const对象的指针形参,而不能将指向const对象的指针传递给指向非const的指针形参。

        const形参:

        如果函数的形参是非const形参,则既可以传递const实参,也可以传递非const实参:这是因为初始化是复制了初始化式的值,所以可以用const对象初始化非const对象,反之亦然。

        如果函数的形参是const形参,则既可以传递const实参,也可以传递非const实参。

        令人吃惊的是:尽管函数的形参是const,但是编译器却将函数的定义视为其形参被声明为普通的非const类型:

        反正编译器对形参是否是const类型不加区别:

        引用形参:

        我们知道:当需要在函数中修改实参的值,或者需要传递大的对象,或者没办法实现对象的复制时,要用到引用形参。

        在传递大对象时,如果不希望修改对象的值,使用const引用可以避免修改实参。如果引用形参的唯一目的是为了避免复制实参,则应将形参定义为const引用。

        当需要修改引用的对象时,函数的形参只是非const引用类型,则非const引用只能与完全相同的非const对象相关联。这样就限制了函数的使用,即只能传递非const类型的对象,所以应该将不修改相应实参的形参定义为const引用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值