Item 03:尽可能使用const

苏芳

Item 03: Use const whenever possible


关键字const

const允许你指定一个语义约束(也不怕是指定一个“不拖欠吃更改”的靶子),而编译器会强制执行这项约束。如果您想被某值保持不更换,就应当明白说出来,因为说出去就是得抱编译器的鼎力相助,确保及时长长的框不吃违反。

重点字const多才多艺。你可用它当classes外部修饰globalnamespace作用域中的常量,或修饰文件函数、或段作用域中让声称也static的对象。你啊得据此其修饰classes内部的staticnon-static成员变量。面对指针,你吗可以指出指南针自身指南针所指物,或二者都(或都未)是const:

char greeting[] = "Hello";            
char* p = greeting;                    //non-const pointer, non-const data
const char* p = greeting;              //non-const pointer, const data
char* const p = greeting;              //const pointer, non-const data
const char* const p = greeting;        //const pointer, const data

若要字const出本星号左边,表示为指物是常量;如果起于星号右边,表示指南针自身是常量;如果起于星号两止,表示被指物指针两者都是常量。

只要为指物是常量,有些人会晤将其写在项目之后、星号之前。两栽写法的义一样,所以下列两独函数接受之参数类型是同样的:

void f1(const Widget* pw);        //f1获得一个指针,指向一个常量的Widget对象
void f2(Widget const *pw);        //f2也是

星星栽形式都有人用,你应该尝试着习惯她。

const面对STL迭代器

STL迭代器就是盖指针也根据塑模出来的,所以迭代器的意向就是像个T*指针。

扬言也迭代器为const就像声明指针也const一样(即宣称一个T*
const指针),表示此迭代器不得对不同之东西,但它所依的事物的值时可以转移的。

假定您盼迭代器所据的东西不足吃改(即STL模拟出一个const
T*指南针),你要的是const_iterator:

std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin();       //iter的作用像个T* const
*iter = 10;                                                //没问题,改变iter所指物
++iter;                                                    //错误!iter是const

const std::vector<int>::const_iterator cTter = vec.begin();     //cIter的作用像个const T*
*cTter = 10;                                                    //错误!*cIter是cosnt
++cTter;                                                        //没问题,改变cIter。

const面向函数声明

const最富有威力的用法是冲函数声明时的动。在一个函数声明式内,const可以与函数返回值各参数函数自身(成员函数)产生关联。

函数返回值

叫函数返回一个常量值,往往可以退因客户错误而致使的意料之外,而与此同时未必放弃安全性以及高效性。举个例子:

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都是坐类型,这样的代码就是匪合法。而一个“良好的自定义类型”的风味是她该与搭类型兼容,因此同意对片价就积做赋值动作虽没什么意思,将operator*的扭动传值声明也const就得防那个“没意思的赋值动作”。

函数形参

于const参数,它们可是就是比如local
const对象同,你该在必要运用它的早晚以她。除非您发得转移参数或local对象,否则要用它们声明也const,只不过多从6单字符而已。

const成员函数

拿const实施于成员函数的目的,是为确认欠成员函数可图为const对象身上。这类似成员用要,两独理由:

  • 她一旦class接口比较易于受清楚,因为,得知哪个函数可以变动对象内容一经谁不函数异常,这一点凡是杀主要之。
  • 其而“操作const对象”成为可能。

简单单成员函数如果只是常量性不同,可以叫重载

冲成员函数是否也const,可以重载一个分子函数;同样地,基于一个指针形参(或者引用形参)是否也对const,可以重载一个函数。const对象只能用const成员。非const对象可以使无一成员,但非const版本是一个再好之相当
————《C++ Primer》第四版 P442

bitwise constness 和 logical constness

成员函数如果是const意味着什么?这里产生零星只流行概念:bitwise
constness
(又称physical constness)和logical constness

bitwise constness

bitwise
constness阵营的口信任,成员函数只有以未改对象的其他成员变量时才方可说凡是const。也就是说它不更改对象内的其余一个bit。bitwise
constness正是针对C++常量性的概念,因此const成员函数不可以变更对象内任何non-static成员变量。

不幸的凡多多益善分子则不敷具备const性质却能通过bitwise测试。更具体地说,一个反了“指针所指物”的成员函数虽然不能够算是const,但若只有指针(而休该所指物)隶属于对象,那么称这个函数为bitwise
constness不见面掀起编译器异议。

class CTextBlock{
public:
    ...
    char& operator[](std::siez_t position) const    //bitwise const声明,但其实不适当
    { return pText[position];}

private:
    char* pText;
};

operator[]贯彻代码并无又改pText。于是编译器很开心地啊operator[]起出目标码。它是bitwise
const,所有编译器都这样肯定,但是,这个class不适当地将该operator[]宣示也const成员函数,而该函数却回一个reference指向对象中价,于是便:

const CTextBlock cctb("Hello");        //声明一个常量对象。
char* pc = &cctb[0];                //调用const operator[]取得一个指针,指向cctb的数据。
*pc = 'J';                            //cctb现在有了“Jello”这样的内容。

就间自然不拖欠出另外错误:你创造一个常量对象并使为某值,而且仅针对她调用const成员函数。但是若毕竟还是改变了其的价值。这种状态便是所谓的logical
constness。

logical constness

随即一端拥护者主张,一个const成员函数可以改其所处理的靶子内的少数bits,但只来于客户端侦测不生之状况下才得如此。

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成员函数内不能赋值给textLength
        lengthIsValid = true;                //和lengthIsValid
    }
    return textLength;
}

length的兑现自不是bitwise
const,因为textLength和lengthIsValid都或吃改。这点儿码成员修改对const
CTextBlock对象而言则只是领,但编译器不容许。它们坚持bitwise
constness。怎么处置?
解决办法很简单:mutable(可变的)、mutable释放掉non-static成员变量的bitwise
constness约束:

class CTextBlock{
public:
    ...
    std::size_t length() const;
private:
    char* pText;
    mutable std::size_t textLength;        //这些成员变量可能总是会被更改,即使在
    mutable bool lengthIsValid;            //const成员函数内
};
std::size_t CTextBlock::length() const
{
    if (!lengthIsValid) {
        textLength = std::strlen(pText);    
        lengthIsValid = true;                
    }
    return textLength;
}

在const和non-const成员函数中避免再次

对此“bitwise-constness非自己想如果”的问题,mutable是只解决办法,但其不能够化解有的const相关难题。举个例子,假设TextBlock内之operator[]不但只有是回一个reference指向某字符,也尽边界减压、日志访问信息、甚至可能进行数量完善性检验。把拥有这些以加大上const和non-const
operator[]遭逢,导致这样的怪:

class TextBlock{
public:
    ...
    const char& operator[](std::siez_t position) const
    { 
        ...            //边界检验
        ...            //日志数据访问
        ...            //检验数据完整性
        return pText[position];
    }
    char& operator[](std::siez_t position)
    { 
        ...            //边界检验
        ...            //日志数据访问
        ...            //检验数据完整性
        return pText[position];
    }
private:
    char* pText;
};

您可知说发中起的代码重复与陪同的编译时间、维护、代码膨胀等使人头疼的题目呢?你实在该举行的是operator[]的法力一浅并行使它们两糟。也就是说,你得令中一个调用另一个。这促使我们以常量性转除

令non-const
operator[]调用其cosnt兄弟是一个幸免代码重复的平安做法——即使过程被需一个转型动作。

class TextBlock{
public:
    ...
    const char& operator[](std::siez_t position) const    //不变
    { 
        ...
        ...
        ...
        return pText[position];
    }
    char& operator[](std::siez_t position)                //现在只调用const op[]
    { 
        return const_cast<char &>(                        //将op返回值的const转除
            static_cast<const TextBlock&>(*this)          //为*this加上const
                [position]                                //调用const op[]
        );
    }
private:
    char* pText;
};

这边共有简单坏转型:第一差用来也*this添加const,第二软是由coant
operator[]的返值备受易除const。
添加const的那么同样不良转型强迫进行了平不行安全转型(将non-const对象转为const对象),所以我们用static_cast。移除const的百般动作就可利用const_cast完成。

虽这个代码看起有些好看,但是贯彻了“避免代码重复”的功效,为了达成这目标一旦写来如此无耻的语法是否值得,只有你自己决定,但“运用const成员函数实现有其non-const孪生兄弟”的艺是值得询问之。更值得询问的是,反向做法——令const版本调用non-const版本,这是一个错误行为。


NOTE

  • 以或多或少事物声明也const可帮编译器侦测出荒唐用法。const可让施加于其他作用域内的靶子、函数参数、函数返回路、成员函数本体。
  • 编译器强制实施bitwise
    constness,但若编写程序时应该以“概念上之常量性”。
  • 当const和non-const成员函数有着本质等价格的落实时,令non-const版本调用const版本可免代码重复。

Effective C++