[笔记]Effective C++ 条款3:尽可能使用const

T* const 表示不可指向不同的东西 但指向的东西的值可以改变

const T* 表示不可改变指向的东西的值 但可以指向不同的东西

T* const p (const修饰p p为指针 被const修饰后不该被改动 指针p不能改变 但指向的内容*p可被改变)

const T* p (const修饰*p *p为不该被改动的对象)

STL迭代器:

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

const成员函数

两个成员函数如果只是常量性(constness)不同,可以被重载。

logical constness,主张一个const成员函数可以修改他所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才得如此。例如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){
        textLength = std::strlen(pText);	//错误!在const成员函数内不能赋值
        lengthIsValid = true;		//    给textLength和lengthIsValid
    }
    return textLength;
}

解决办法:利用C++的一个与const相关的摆动场: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成员函数中避免重复

class TextBlock{
public:
    const char& operator[](std::size_t position) const{
        	//边界检查(bounds checking)
         	//志记数据访问(log access data)
          	//检验数据完整性(verify data integrity)
           return text[position];         		    
    }
    char& operator[](std::size_t position){
           //边界检查(bounds checking)
         	//志记数据访问(log access data)
          	//检验数据完整性(verify data integrity)
           return text[position];
    }
private:
    std::string text;
}

以上有代码重复以及伴随的编译时间、维护、代码膨胀等问题。真正该做的是实现operator[]的机能一次并使用它两次。即在其中一个调用另一个。将常量性转除(casting away constness)

class TextBlock{
public:
    const char& operator[](std::size_t position) const{
        	//边界检查(bounds checking)
         	//志记数据访问(log access data)
          	//检验数据完整性(verify data integrity)
           return text[position];         		    
    }
    char& operator[](std::size_t position){	//现在只调用const op[]
           //边界检查(bounds checking)
         	//志记数据访问(log access data)
          	//检验数据完整性(verify data integrity)
           return 
               const_cast<char&>(				//将op[]返回值的const转除
                    static_cast<const TextBlock&>(*this)    //为*this加上const
                	    [position]	 );		    //调用const op[]
    }
private:
    std::string text;
}

这份代码有两个转型动作。在non-const operator[]内部若只是单纯调用operator[],会递归调用自己。所以必须明确的指出调用的是const operator[],但C++缺乏直接的语法可以这样做。因此这里将*this从原始类型TextBlock&转型为const TextBlock&。所以这里有两次转型:第一次用来为*this添加const(这使接下来调用operator[]时得以调用const版本),第二次从const operator[]的返回值中移除const。

反向做法(const成员函数调用non-const成员函数)是错误行为:因为对象有可能因此被改动。

  1. 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
  2. 编译器强制实施bitwise constness,但编写程序时应该使用“概念上的常量性”(conceptual constness)。
  3. 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
上一篇:Operator 示例:使用 Redis 部署 PHP 留言板应用程序


下一篇:CF13B