2021-08-20

条款03:尽可能使用const
  1. const修饰变量。

    场景:如果变量本身不应该被修改,应该使用const修饰。这样编译器可以进行保护,确保这个变量不会被修改。

    • 关键字const出现在星号左边,表示被指物是常量。
    • 关键字const出现在星号右边,表示指针自身是常量。
  2. const修饰函数。

    • 修饰参数时,和修饰一般变量相同。

    • 修饰返回值,可以降低因客户错误而造成的意外。

      A a, b, c;     // 类类型
      ... 
      if (a * b = c) {//其实是想做一个比较动作,使用const修饰返回值可以避免这种错误, a*b的返回值是const, 变量c不能赋值给a*b。
          ... 
      }
      

      若a和b都是内置类型,编译器会直接报错。而一个“良好的用户自定义类型”的特征是他们避免与内置类型不兼容。因此对operator*的定义应该如下:

      const A operator*(const A& a, const A& b);
      
  3. const修饰成员函数。

    const修饰成员函数有2个好处:

    • 可读性:使得接口容易被理解,一眼就可以看出这个接口能否修改对象。

    • const修饰的成员函数可以作用于const对象。

      class Array
      { 
      public: 
          char& operator[](size_t pos) const //bitwise constness声明
          { return a[pos]; }    //但其实不恰当
          
      private:
          char* a;
      };
      
      const Array arr("Hello"); //声明一个常量对象
      char *pc = &arr[0];       //调用const operator[]取得一个指针,指向arr的数据
      *pc = 'J';                //arr现在有了“Jello”的内容。
      

      还有一种logical constness:一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才行:

      class Array 
      {
      public:
          size_t length() const;
      private:
          char *p;
          size_t length; //长度
          bool lenIsVal; //长度是否有效
      };
      
      size_t Array::length() const {
          if (!lenIsVal) {
              length = strlen(p); //错误!在const成员函数内不能修改对象
              lenIsVal = true;    //错误!在const成员函数内不能修改对象
          }
          return length;
      }
      

      但是,C++对常量性的定义是bitwise constness的,所以这样的操作非法。

      解决办法是使用mutable:

      class Array {
      public: 
          size_t length() const;
          
      private:
          char *p;
          size_t count;          // 计数
          mutable size_t length; // length总是会被更改,即使在const成员函数内
          mutable bool lenIsVal; // lenIsVal总是会被更改,即使在const成员函数内
      };
      
      size_t CTextBlock::length() const {
          if (!lenIsVal) {
              length = strlen(p); //length可以在任何成员函数中修改,即使是const成员函数
              lenIsVal = true;    //lenIsVal可以在任何成员函数中修改,即使是const成员函数
          	count ++ ; //错误!const成员函数内,不能修改对象。
          }
          return length;
      }
      

      总的来说,上面提到了2种“修改”const成员函数中修改对象(修改const对象)的方法
      最后,const和non-const版本的函数可能含有重复的代码,如果抽离出来单独成为一个成员函数还是有重复。如果希望去重,可以使用“运用const成员函数实现出其non-const孪生兄弟”的技术:

      class Array { 
      public: 
          const char& operator[](size_t pos) const { 
              ... 
          } 
          
          char& operator[](size_t pos) { 
              return const_cast<char&>( //移除返回值的const属性
                  static_cast<const CTextBlock&>(*this) //强转对象为const对象
                  [pos] //调用op[]
              );
          }
      };
      

      static_cast 相当于传统的C语言里的强制转换,该运算符把expression转换为new_type类型,用来强迫隐式转换,例如non-const对象转为const对象,编译时检查,用于非多态的转换,可以转换指针及其他,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:

      ①用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。

      进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;

      进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。

      ②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。

      ③把空指针转换成目标类型的空指针。

      ④把任何类型的表达式转换成void类型。

      注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性。

注意:本文参考的是《effective c++》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值