const的奥妙

      首先需要说明的是这篇文章是读完《Effective C++》条款3后写的,文章中的很多内容都是摘自该文章。

 

一、const的语法

 

       const为程序提供了一个语义约束,它会告诉编译器和其它程序员某值是应该保持不变的。它带来的好处就是使得编译器可以检查出那些修改了应保持不变的常量的错误,从而减轻了程序员的负担。const可在classes外部修饰globalnamespace作用域中的常量,或修饰文件、函数、或区块作用域中被声明为static的对象,还可以用它来修饰classes内部的staticnon-static成员变量。其具体用法如下:

       const int a = 1; // a是值为1的常量

       

       float f = 3.14;

       const float& rf = f; // rf是值为3.14的常量,不能通过它修改 f 的值。

 

      char text[] = "hello";

      char* const pt = text;                  // pt是常量指针,不能改变,但是可以修改所指物的值

      const char* pt = text;                  // pt可被修改,但是所指物不能被修改

      const char* const pt = text;         // 两者都不能被修改

  

      判断const修饰的指针的常量性时可以用此方法:如果const出现在*左边,表示被指物是常量;如果const出现在*右边,表示指针是常量;如果出现在*两边,表示被指物和指针都是常量。因此,一下两种写法意思相同:

      const Widget* pw = new Widget;  // 指向一个常量的Widget 对象

      Widget const* pw = new Widget;  // 同上

 

      虽然const约束可以通过const_cast去除,从而使得可以修改const对象的值。但是去除const约束是显式的,它的使用是通过了程序员的思考,并告知了程序员其常量性的,因此还是尽可能的减少了对常量的错误修改。

 

二、const成员函数

 

       const实施于成员函数的目的,是为了确认该成员函数可作用于const对象上。这类成员函数之所以重要,基于两个理由:1、它们使class接口比较容易理解。2、它们使操作const对象成为可能。而改善C++程序效率的一个根本办法就是以pass by reference-to-const方式传递对象,而此技术的前提就是有const成员函数来处理取得的const对象。考虑如下代码:

      class TextBlock {

      public:

           ...

       private:

           std::string text;

      };

 

      // TextBlock对象可能存在的某些应用

 

     void print(const TextBlock& ctb)

     {

          std::cout << ctd[0];

          ...

      }

 

       ...

      

      tb[0] = 'a';

 

      print()的实现中采用了pass by reference-to-const的方式传递对象,因此要使ctd[0]通过编译,需要提供一个const operator[];而要使tb[0]='a'通过编译,则需要提供一个non-const operator[]。所以TextBlock需要增加下面两个成员函数:

    const char& operator[](std::size_t position) const;

    char& operator[](std::size_t position);

   

    上面两个函数都是返回的reference to char,而不是char。是因为如果函数的返回类型是个内置类型,那么改动函数返回值从来就是不合法的,所以像tb[0]='a'是不能通过编译的。在const operator[]的声明中,函数返回值的类型添加了const约束,是因为使用const operator[]的目的是为了确保操作的常量性。

 

三、在constnon-const成员函数中避免重复

 

      对于前面提到的operator[]的实现,可能只需要语句return text[position]即可。但是随着需求不断地完善和改变,可能会在函数返回前添加许多操作,如边界检查、数据完整性检验等等。当再次实现constnon-const两个版本的operator[]时,将会发现二者的代码尽是完全一样的。这无疑会对程序的维护带来问题。因此,需要避免二者在实现时所带的代码重复。其解决的办法就是实现operator[]一次,然后让另外一个调用它。这时候又带来一个新抉择的问题:是non-const版本调用const版本的operator[],还是const版本调用non-const版本的operator[]?记住,const成员函数承诺绝不改变其对象的逻辑状态,non-const成员函数却没有这般承诺。如果在const函数内调用non-const函数,就是冒了这样的风险:对象有可能被改动了。而反向的调用才是安全的:non-const成员函数本来就可以对其对象内做任何动作,所以其中调用一个const成员函数并不会带来风险。因此,下面是修改后的operator[]实现代码:

       const char& TextBlock::operator[](std::size_t position) const

       {

            ...

            return text[position];   //一如既往

       }

 

       char& TextBlock::operator[](std::size_t position)

       {

            ...

            return const_cast<char&>( static_cast<const TextBlock>(*this)[position];

       }

 

       non-const operator[]的实现中,有两个转换动作:1、将*this从其原始类型TextBlock&转换为const TextBlock&。这是因为C++缺乏可以直接显示调用const operator[]的语法。2、从const operator[]的返回值中移除const

 

四、bitwise constnesslogical constness

     

      bitwise constness:成员函数只是在不更改对象之任何成员变量时才可以说是const。也就是它不更改对象内的任何一个bit。这种论点的好处是很容易侦测违反点:编译器只需要寻找成员变量的赋值动作即可。bitwise constness正是C++对常量性的定义,因此const成员函数不可以更改对象内任何non-static出院变量。

 

      logical constness:一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才得如此。例如CTextBlock class有可能高速缓存文本区的长度以便应付询问:

       class CTextBlock{

       public:

           ...

           std::size_t  length() const;

       private:

           char* ptext_;

           std::size_t textLength_;         // 最近一次计算的文本区块长度

           bool lengthIsValid_;                // 目前的长度是否有效

           ....

       };

 

        std::size_t CTextBlock::length() const

         {

             if(!lengthIsValid_) {

                 textLength_ = std::strlen(ptext_);  // 错误!在const成员函数内不能赋值给

                 lengthIsValid_ = true;                      // textLength_lengthIsValid_

             }

         }

      

       要使上述的length()方法通过编译,怎么办呢?利用C++的一个与const相关的摆动场:mutable,它释放掉non-static成员变量的bitwise constness约束。因此对CTextBlock修改如下:

       class CTextBlock{

       public:

           ...

           std::size_t  length() const;

       private:

           char* ptext_;

           mutable std::size_t textLength_;         // 这些成员变量可能总是会被更改,

           mutable bool lengthIsValid_;                // 即使在const成员函数内

           ....

       };

 

五、结束

 

       最后,感谢Scott Meyers为我们带来如此详细而又生动的论述。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值