Effeictive C++ 条款03 尽可能使用const

     ①  STL迭代器是以指针为根据熟模出来的,所以迭代器的作用就像是个T*指针。声明迭代器为const就像声明指针为const一样(即声明一个T* const),我们表示这个迭代器不能改变它的指向,但它指向的东西是可以改变的。如果你要求这个迭代器所指向的东西不能改变的话,则应当使用const_iterator。

    ②  const最具威力的用法是面对函数声明时的应用。在一个函数声明式内,const可以和函数返回值、各参数、函数本身(如果是成员函数)产生关联。

      令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而不至于放弃安全性和高效性。举个例子,考虑有理数的operator*声明式:

class Rational{};

const Rational operator*(const Rational& lhs,const Rational& rhs);

第一个看到这个声明式很多人可能看不出什么端倪可能会疑问为什么要返回const对象呢?原因是如果不这样使用这个类的用户就会意外地错误使用:

  Rational a,b,c;

  (a*b)=c;//在a*b的结果上调用operator=

这个动作本身没有价值一般来说也不会那样去使用这个类但当这个式子以下面这种形式出现时就明白了这种错误的用法的来源:

if(a*b=c)//本意可能是a*b==c判断是否相等 但少打了一个=导致出现这种的错误

一个设计良好的类应当可以避免用户错误地使用,通过将返回类型设定为const 对象则可以便面这种错误的赋值行为。

   ③  const成员函数

     将const实施于成员函数的目的,是为了确保const对象可以(也只能)调用这类函数(const成员函数理论上不可以修改任何值至于为什么说是理论上下面会提到),对于两个成员函数如果只是常量性不同,可以被重载。这也是为什么在许多类的设计当中需要提供两个函数尽管代码相同

class TextBlock
{
  public:
     const char& operator[](std::size_t positon)const
{return text[position];
}
     char& operator[](std::size_t position){return text[position];}
private: std::string text;
}
TextBlock tb("Hello");
std::cout<<tb[0];//调用非const版本的[]
const TextBlock ctb("World");
std::cout<<ctb[0];//调用const版本的[]

注意:非const operator[]的返回类型是个引用类型,不是char。如果operator[]只是返回一个char,下面的句子就无法通过编译:

tb[0]='X';

那是因为,如果函数的返回类型是个内置类型,那么改动函数返回值从来就不合法。纵使合法,当返回类型为对象时实际上返回的是该对象的一个副本,这个对象只存在于该语句生存期内,语句结束后也就析构了。

   ④  bitwise constness(physical constness)和logical constness

      现在让我们来解释为什么之前说const成员函数是理论上不修改值,对于bitwise阵营的人相信,成员函数只有在不更改对象之任何变量(static除外)时才可以说是const。也就是说它不更改对象内的任何一个bit。这种论点的好处是好容易侦测违反点:编译器只需寻找成员变量的赋值动作即可。bitwise constness正是C++对常量性(constness)的定义,因此const成员函数不可以更改对象内任何non-static成员变量。

     不幸的是许多成员函数虽然不十足具备const性质却能通过bitwise测试。更具体地说,一个更改了“指针所指物”的成员函数虽然不能算是const,但如果只有指针(而非其所指物)隶属于对象,那么称此函数为bitwise const 不会引发编译器异议:


class CTextBlock
{
 public:
  char& operator[](std::size_t position)const{
return pText[position];
}
private:
   char* pText;
}

这个类不适当地将其operator[]声明为const成员函数,而该函数却返回一个引用指向对象内部值。请注意operator[]实现代码并不更改pText。于是编译器很开心地为operator[]产出目标码。它是bitwise const,所有编译器都这么认定,但是它却导致一个隐式的错误:

const CTextBlock cctb("Hello");

char* pc=&cctb[0];

*pc='J';

你声明const对象初衷是不想更改值却通过const成员函数返回了一个可以改变值得接口。

     由于这种情况导出所谓的logical constness。这一派拥护者主张,一个const成员函数可以修改它所处理的对象内的某些bits
⑤    通过转型来避免代码重复

class TexkBlock
{
  public:
    const char& operator[](std::size_t position)const
{.................//略去前面的代码
return text[position];}
char& operator[](std::size_t position)
{
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}
}
需要注意的是:不应该令const版本调用非const版本来避免代码重复。记住const成员函数承诺绝不改变对象的逻辑状态,非const成员函数却没有这种承诺。如果在const函数内调用non-const函数,就是冒了这样的风险:你曾经承诺不该懂的那个对象被改动了。这就是为什么"const 成员函数调用non-const成员函数"是一种错误行为:因为对象有可能因此被改动。实际上若要令这样的代码通过编译,你必须使用一个const_cast将*this身上的const性质解放掉,这是乌云罩顶的清洗前兆。非const成员函数本来就可以对其对象做任何动作,所以在其中调用一个const成员函数并不会带来风险。这就是为什么本例以static_cast作用于*this的原因:这不会存在const的任何危险。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值