条款03: 尽可能使用 const
const是个奇妙且非比寻常的东西。在指针和迭代器上;在指针、迭代器、references指涉的对象上;在函数参数和返回类型上;在local 变量上,在成员函数身上;
一. const 是C++中的对于变量语义约束(不可修改),编译器会强制实行这项约束,只要该值不可被改变(事实),就应该去进行约束
#include<vector>
const int val1=5;
static int val2=5;
const int *pointer1 =&val1; //底层 const 通过pointer1 访问指向的元素可读不可写
int* const pointer2 =&val2; //顶层 const 不可改变pointer1 指向
vector<int> vec;
vector<int>::iterator iter1=vec.begin(); //上层 const 不可改变迭代器 指向
vector<int>::const_iterator iter2=vec.begin(); //顶层 const 通过迭代器访问元素可读不可写
什么是顶层const?什么是底层const?
1 、指向常量的指针:代表不能改变其指向内容的指针。声明时const可以放在类型名前后都可,拿int类型来说,声明时:const int和int const 是等价的。声明指向常量的指针也就是底层const,下面举一个例子:
int num_a = 1;
int const *p_a = &num_a; //底层const
//*p_a = 2; //错误,指向“常量”的指针不能改变所指的对象
注意:指向“常量”的指针不代表它所指向的内容一定是常量,只是代表不能通过解引用符(操作符*)来改变它所指向的内容。上例中指针p_a指向的内容就不是常量,可以通过赋值语句:num_a=2; 来改变它所指向的内容。
2 、指针常量:代表指针本身是常量,声明时必须初始化,之后它存储的地址值就不能再改变。声明时const必须放在指针符号*后面,即:*const 。声明常量指针就是顶层const,下面举一个例子:
int num_b = 2;
int *const p_b = &num_b; //顶层const
//p_b = &num_a; //错误,常量指针不能改变存储的地址值
其实顶层const和底层const很简单,一个指针本身添加const限定符就是顶层const,而指针所指的对象添加const限定符就是底层const。
----------------------------
感觉书里面说的更好,如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针都是常量。
二. const 最具威力的是面对函数声明时的应用, const可以与函数返回值,各参数,函数自身(成员函数)产生关联
-
令函数返回一个常量值,可以降低因客户操作而造成的(意外错误)
namespace eapp { class Rational { const Rational operator* (const Rational &lhs,const Rational &rhs) //重载成员函数返回一个常量值 { return Rational(); } } void test() { Rational a,b,c; if(a*b = c) {}//防止客户的暴行,就不会引起编译错误,但事实上代码是错误的。 } }
-
const 实施成员函数: 确认该成员函数可作用于 const 对象身上。
第一:使得 class接口更加容易被理解,(得知哪些函数可以改动对象内容哪些不行)
第二:使得操作 const 对象成为可能, (pass by reference-to-const 方式传递对象),用const成员函数处理const对象(用魔法打败魔法)。
成员函数如果只是常量性不同,可以被重载
class TextBlock
{
private:
string text_;
public:
const char &operator[](size_t position) const {return text_[position];}
char &operator[](size_t position){return text_[position];}//比上面少了两个const
}
如何实施对对象进行const限制的措施:
- bitwise const阵营:不更改对象内的任何一个 bit (太过于强硬)
- bitwise constness阵营: 对对象的成员变量实施 const(编译器的做法)
- logical constness(重要): 一个const成员函数可以修改它所处理的对象的某些 bits,但请确保客户端侦测不出 (实现办法是利用C++的一个与const相关的摆动场: mutable)
class CTextBlock1
{
private:
char* text_;
public:
//
char &operator[](size_t position)const{return text_[position];}
};
class CTextBlock2
{
private:
char* text_;
mutable size_t text_length_; //使用mutable, 实现 logical constness,规定一些bit可以被修改
mutable bool text_isvaild_;
public:
size_t length() const;
};
size_t CTextBlock2::length() const
{
if(!text_isvaild_)
{
text_length_=strlen(text_);
text_isvaild_=true;
}
return text_length_;
}
在const 和 non-const 成员函数中避免重复(写出重复的代码)
//目的是使用operator[]的机能一次并使用它两次。
class CTextBlock3
{
private:
char* text_;
public:
const char &operator[](size_t position)const{return text_[position];}
char &operator[](size_t position){
return const_cast<char&>(
static_cast<const CTextBlock3&>(*this)[position]
)};//P25详细剖析。
};
总结:
- 将某些东西声明为const能帮助编译器去甄别错误用法
(任意作用域对象,函数参数,函数返回类型,成员函数本体) - 编译器强制实施 bitwise constness,但我们编写程序时应该使用 “概念上的常量性”
- 当const 和 non-const成员函数有着实质等价的实现时,令 non-const 版本调用const版本可避免代码重复。