• Effective C++ 条款03:尽可能使用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 = greeting;  // const pointer, const data
    

    const在*左边,表示被指物是常量,指针所指向的内容不能修改,但是可以修改指针,让指针指向其他对象。

    const在*右边,表示指针自身是常量,指针不能修改,不能指向其他对象,但是当前指向的内容可以修改。

    const在*的两侧,表示指针自身是常量,被指物也是常量,指针不能指向其他对象,当前指向的内容也不能改变。

    场景二 用于对象前、后

    void f1(const Widget* pw);
    void f2(Widget const * pw);
    

    两种表达式的效果是一样的,都在*的左边,说明被指物是常量。

    场景三 用于对STL迭代器的修饰

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

    场景四 用于返回值

    class Rational {...};
    const Rational operator* (const Rational& lhs, const Rational& rhs);
    
    Rational a, b, c;
    ...
    (a * b) = c;    // 显然在把返回结果设置为const以后,就不允许这样的操作发生
    
    if (a * b = c)  // 如果设置返回值为const的时候,这种手误的操作也不会发生
    

    场景五 用于成员函数本体

    const成员函数
    class TextBlock {
    public:
    	'''
    	const char& operator[](std::size_t position) const   // operator[] for const对象
    	{return text[position];}
    	char& operator[](std::size_t position)
    	{return text[position];]}
    private:
    	std::string text;
    }
    // 调用一
    TextBlock tb("Hello");
    std::cout << tb[0];     // 调用non-const TextBlock::operator[]
    const TextBlock ctb("World");
    std::cout << ctb[0];    // 调用const TextBlock::operator[]
    // 调用二
    void print(const TextBlock& ctb) {
    	std::cout << ctb[0];  // 调用const TextBlock::operator[]
    	...
    }
    // 调用三
    std::cout << tb[0];   //没问题,读一个non-const TextBlock
    tb[0] = 'x';          //没问题,写一个non-const TextBlock
    std::cout << ctb[0];  //没问题, 读一个const TextBlock
    ctb[0] = 'x';         //错误,写一个const TextBlock, 错误的原因在于operator[]的返回值为const
    

    注意以上两个函数的返回值都为&,如果返回值是一个char的话,tb[0]= ‘x’;是无法通过编译的;
    那是因为,如果函数的返回类型是个内置类型,那么改动函数返回值从来就不合法。纵使合法,C++以by value返回对象这个事实意味被改动的其实是tb.text[0]的一个副本,不是tb.text[0]自身,那不会是你想要的行为。

    场景六 bitwise constness和logical constness

    class CTextBlock {
    public:
    	...
    	char& operator[](std::size_t position) const     //bitwise const 声明,但其实不适当
    private:
    	char* pText;
    }
    // 调用
    const CTextBlock cctb("Hello");    // 声明一个常量对象
    char* pc = &cctb[0];               // 调用const operator[]取得一个指针,指向cctb的数据
    *pc = 'J';							// cctb现在有了"Jello"这样的内容
    // 说明
    // const 修饰函数体,说明函数体内不能修改任何non-static成员变量,在函数题内却是没有修改成员变量,但是最后还是修改成功了,那是因为返回值不是char;bitwise constness的主张是成员变量一个bit都不能修改,以上情况导出所谓的logical constness。
    
    class CTextBlock {
    public:
    	...
    	std::size_t length() const;
    private:
    	char* pText;
    	std::size_t textLength;       //最近一次计算的文本区块长度
    	bool lengthIsValid;           //目前的长度是否有效
    };
    // 成员函数实现
    std::size_t length() const {
    	if (!lengthIsValid) {
    		textLength = std::strlen(pText);  //错误,在const成员函数内不能赋值给textLength和lengthIsValid
    		lengthValid = true;
    	}
    	return textLength;
    }
    

    场景七 mutable用法

    // 用mutable(可变的)释放掉non-static成员变量的bitwise constness约束
    class CTextBlock {
    public:
    	...
    	std::size_t length() const;
    private:
    	char* pText;
    	mutable std::size_t textLength;  //这些成员变量可能总是被更改,即使在const成员函数内。
    	mutable bool lengthIsValid;     
    };
    std::size_t CTextBlock::length() const {
    	if (!lengthIsValid) {
    		textLength = std::strlen(pText);
    		lengthIsValid = true;
    	}
    	return textLength;
    }
    

    场景八 const实现non-const成员函数

    在const和non-const成员函数中避免重复
    class TextBlock {
    public:
    	...
    	const char& operator[](std::size_t position) const {
    		...    // 边界检查
    		...    // 日志记录数据 
    		...    // 检验数据完整性
    		return text[position];
    	}
    	char& operator[](std::size_t position) {
    		...   // 边界检查
    		...   // 日志记录数据
    		...   // 检验数据完整性
    		return text[position];
    	}
    private:
    	std::string text;
    };
    // 可以看到以上有非常严重的代码重复, 改进就是用const operator[] 实现 non-const operator[]
    class TextBlock {
    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_cast转型是因为返回类型,static_cast转型是为了转成const对象。
    	}
    };
    

    总结

    1. 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
    2. 编译器强制实施bitwise constness,但你编写程序时应该使用"概念上的常量性(conceptual constness)"。
    3. 当const和non-const 成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复.
  • 相关阅读:
    构造方法,析构函数
    Redis Sentinel实现的机制与原理详解
    关于PageRank的总结
    再次建立wordpress
    图的研究杂记
    并行的论文
    还是bib问题
    如何解决bib的一些问题
    忙中记录
    近期一些学术相关记录
  • 原文地址:https://www.cnblogs.com/zhonghuasong/p/7290712.html
Copyright © 2020-2023  润新知