Effective C++学习笔记总链接
改善程序与设计的55个具体做法学习笔记-每日1条
条款03:尽可能使用const
【技巧】
1.将某些东西声明为const 可帮编译器侦测出错误用法。
2. const尽可能施加于任何作用域内的对象,函数参数,函数返回类型,成员函数本体。
3. 编写程序时,应该使用“概念上的常量性”(mutable)
4. 当const和non-const 成员函数有着实质等价的实现时,令non-const 版本调用const 版本可避免代码重复。
const 指针
char greeting[] = "Hello";
const char* p = greeting; // const data
char const * p = greeting; // const data ,与上式意义相同
char* const p = greeting; // const pointer
const char* const p = greeting; // const pointer const data
const 出现在星号的左边,表示被指物是常量。
const 出现在星号的右边,表示指针自身为常量。
const 出现在星号的两边,表示被指物和指针两者都为常量。
const STL迭代器
迭代器的作用就像个T* 指针,const 迭代器相当于T* const指针,代表迭代器不得指向不同的东西。如果希望迭代器所指的东西值不变,需要const_iterator
std::vector<int> vec;
...
const std::vector<int>::iterator iter = // iter 相当于 T* const
vec,begin();
*iter = 10; // ok
iter++; // error, iter是const
...
std::vector<int>::const_iterator citer = // citer 相当于 const T*
vec,begin();
*citer = 10; // error, *citer是const
citer++; // ok
函数返回值为const
令函数返回一个常量值,往往可以降低因客户错误而造成的意外。
class Rational{...};
const Rational opterator* (const Rational&, const Rational&);
...
Rational a,b,c;
(a*b) =c ;// 错误
除非你需要改动参数或local const对象,否则请将他们声明为const.
const 成员函数
const成员函数,是为了确认该成员函数可作用于const对象
第一:使得class接口比较容易被理解。哪个函数可以改动对象内容,而哪个函数不行。
第二:使操作const对象成为可能,这是编写高效代码的关键。
改善C++程序效率的一个根本办法是以 pass-by-reference-to-const 方式传递对象。(条款20)
两个成员函数如果只是常量性不同,可以被重载。
const char& operator[](std::size_t position) const // operator[] for const对象
char& operator[](std::size_t position) // operator[] for non-const对象
const成员函数mutable
在const成员函数中,mutable 成员变量可以被更改
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成员函数中避免重复
上述两个版本的operator[],可能存在代码重复
真正该做的是实现operator[] 的机能一次并使用它两次。也就是令其中一个调用另一个。这就促使我们将常量性转除。
转型是个糟糕的想法(条款27),但是代码重复更加糟糕。
令non-const版本 调用 const 版本
因此,令non-const operator[] 调用其const 兄弟是一个避免代码重复的安全做法。即使过程中需要一个转型动作。
class TextBlock{
public:
...
const char& operator[](std::size_t position) const // operator[] for const对象,一如既往
char& operator[](std::size_t position) // operator[] for non-const对象
{
//将op[] 返回值的const转除为*this加上const调用const op[]
// 两次转型,下面解释
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}
}
用non-const op[] 调用其const兄弟,如果non-const op[] 内部只是单纯的调用operator[],会递归调用自己。
我们必须明确指出调用的是const op[],但C++缺乏直接的语法可以那么做。
因此,我将*this 从其原型 TextBlock& 转化为 const TextBlock&
使用转型操作为它加上const!
这里两次转型:
第一次用来为*this 添加const(这使接下来调用operator[] 时得以调用const版本)
第二次则是从const op[] 的返回值中移除 const
令const版本调用non-const版本是一种错误行为
const 成员函数承诺绝不改变其对象的逻辑状态,non-const 成员函数却没有这种承诺。
如果const函数调用non-const函数,就会冒风险:你曾经承诺不改动的那个对象被改动了。