美文网首页C++
[C++] const关键字

[C++] const关键字

作者: 何幻 | 来源:发表于2017-05-03 13:42 被阅读34次

    关键字const多才多艺,
    你可以用它在class外部修饰global或namespace作用域中的常量,
    或修饰文件,函数,或区块作用域(block scope)中被声明为static的对象。
    你也可以用它修饰class内部的static和non-static成员变量。
    面对指针,你也可以指出指针自身,指针所指物,或者两者(或都不)是const。

    1. 指针

    STL迭代器系以指针为根据塑模出来,所以迭代器的作用就像个T*指针,
    声明迭代器为const就像声明指针为const一样,即声明一个T* const指针,
    表示这个迭代器不得指向不同的东西,但它所指的东西的值是可以改动的。
    如果你希望迭代器所指的东西不可以改动,即希望STL模拟一个const T*指针,
    你需要的是const_iterator

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

    2. 函数

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

    2.1 返回值

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

    class Rational { ... };
    const Rational operator* (const Rational& lhs, const Rational& rhs);
    

    为什么返回一个const对象呢?原因是如果不这样客户就能实现这样的暴行:

    Rational a,b,c;
    ...
    
    // 在 a * b 的成果上调用 operator=
    (a * b) = c;
    

    operator*的回传值声明为const,可以预防那个“没意思的赋值动作”,这就是该这么做的原因。

    2.2 参数

    至于const参数,没有什么特别新颖的观念,
    他们不过就像local const对象一样,你应该在必要使用它们的时候使用它们。
    除非你有需要改动参数或local对象,否则请将它们声明为const。
    只不过多打6个字符,却可以省下恼人的错误。

    2.3 成员函数

    将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上。
    这一类成员函数之所以重要,基于两个理由。
    第一,它们似class接口比较容易被理解,这是因为,得知哪个函数可以改动对象内容而哪个函数不行,很是重要。
    第二,它们使“操作const对象”成为可能,这对编写高效代码是个关键。

    (1)成员函数重载

    许多人漠视一件事实:
    两个成员函数如果只是常量性不同,可以被重载。
    这实在是一个重要的C++特性。

    考虑以下class,用来表现一大块文字:

    class TextBlock{
    public:
        ...
    
        // operator[] for const对象
        const char& operator[] (std::size_t position) const{
            return text[position];
        }
    
        // operator[] for non-const对象
        char& operator[] (std::size_t position){
            return text[position];
        }
    
    private:
        std::string text;
    };
    

    TextBlockoperator[]可被这样使用:

    TextBlock tb("Hello");
    
    // 调用non-const TextBlock::operator[]
    std::cout << tb[0];
    
    const TextBlock ctb("World");
    
    // 调用const TextBlock::operator[]
    std::cout << ctb[0];
    

    只要重载operator[]并对不同的版本给予不同的返回类型,就可以令const和non-const TextBlock获得不同的处理:

    
    // 没问题,读一个non-const TextBlock
    std::cout << tb[0];
    
    // 没问题,写一个non-const TextBlock
    tb[0] = 'x';
    
    // 没问题,读一个const TextBlock
    std::cout << ctb[0];
    
    // 错误,写一个const TextBlock
    ctb[0] = 'x';
    

    注意,上述错误只因operator[]的返回类型所致,
    错误起因于企图对一个“由const版之operator[]返回”的const char&施行赋值动作。
    也请注意,non-const operator[]的返回类型是一个reference to char,不是char,
    如果operator[]只是返回一个char,下面这样的句子就无法通过编译。

    tb[0] = 'x';
    

    那是因为,如果函数的返回类型是个内置类型,那么改动函数返回值从来就不合法。
    纵使合法,C++以by value返回对象这一事实,意味着被改动的其实是tb.text[0]的一个副本,不是tb.text[0]自身。

    (2)bitwise constness & logical constness

    成员函数如果是const意味什么?
    这有两个流行概念:bitwise constness(又称physical constness),和logical constness。

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

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

    这种情况导出所谓的logical constness,
    这一派拥护者主张,一个const成员函数可以修改它所处理的对象内的某些bit,但只有在客户端侦测不出情况下才得如此。

    例如,CTextBlock class有可能高速缓存(cache)文本区块的长度以便应付询问:

    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){
    
            // 错误,在const成员函数内,不能赋值给textLength和lengthIsValid
            textLength = std::strlen(pText);
            lengthIsValid = true;
        }
    
        return textLength;
    }
    

    length的实现,当然不是bitwise const,因为textLengthlengthIsValid都可能被修改。
    这两笔数据被修改对const CTextBlock对象而言虽然可接受,但编译器不同意。
    它们坚持bitwise constness,怎么办?

    解决办法很简单,利用C++的一个与const相关的摆动场,mutable(可变的)。
    mutable释放掉non-static成员变量的bitwise constness约束。

    class CTextBlock{
    public:
        ...
    
    private:
        // 这些成员变量可能总是会被改变,即使在const成员函数内
        mutable std::size_t textLength;
        mutable bool lengthIsValid;
    };
    

    Effective C++ - P17

    相关文章

      网友评论

        本文标题:[C++] const关键字

        本文链接:https://www.haomeiwen.com/subject/ziektxtx.html