条款03:尽可能使用const

1、为什么尽可能使用const?
const指定语义约束,即某个对象不应该被改变,并且编译器会强制执行这条约束。如果某个对象确实不应该被改变,那么我们就应该将其生命为const,这样一来,如果违背了这个约束,编译器将帮助我们检测出来。

2、界定常量指针和指向常量的指针

const Type * pt;//(1)
Type const *pt;//(2)
Type* const pt;//(3)

(1)和 (2) 表达的意思是一样的,即指向常量的指针。而(3) 表示常量指针。也就是二者的界定点在与constzai星号前还是后,跟类型前后无关。

3、迭代器与指针的关系
迭代器是由指针塑模出来的,因此迭代器的作用就相当于是一个T*类型的指针,如果迭代器指向不可改变的数据:

const std::vector<int>::iterator iter;

如果表达迭代器不可改变指向(自身为const):

std::vector<int>::const_iterator iter;

4、const与函数声明式
const的最大威力便对函数声明时的应用,在一个函数声明式内,const可以和:函数返回值、各个参数、函数自身产生关联。
5、const 与函数返回值
令函数返回一个常量值往往可以减少错误。
例如:重载乘号时(*):

const Rational operator *(const Rational& lhs,const Rational& rhs)
Rational a,b,c;
(a*b)=c;

如果符号函数的返回值不是const的,那么执行:

(a*b)=c;

时将会导致错误,这相相当于对两个数的乘积的结果做赋值。
再例如:

if(a*b=c);//本来想写成a*b==c 

如果a、b是内置类型,这样的结果直接就是错误的。自定义类型应该更好的内置类型兼容,因此对两个数的乘积做赋值也应该表达为非法。而将返回值设置为const就可以表达出这种非法效果,让编译器识别出来。
6、const成员函数
(1)const成员函数的有两个重要作用:

  • 确定哪个函数可以改变对象内容,哪个不行。让class接口更容易理解。
  • 将const作用于成员函数的目的是确认该成员函数可作用于const对象上面。 使得操纵const对象变得可能。它支撑以pass by reference-to-contst方式传递对象。而pass by reference-to-contst 正是改善C++程序效率的根本方法。实现这种传递方式的前提便是有const函数可用来处理取得(并经修饰)的const对象。

(2)同签名的const函数与non-const成员函数的关系
const成员函数与non-const成员函数构成重载关系,这一点非常重要。当函数传递的是一个const对象时,调用const成员函数,如果没有这样的成员函数就会报错。当函数传递的是一个non-const对象时,调用non-const成员函数,如果没有这样的成员函数就会报错。

(3)成员函数声明为const意味着什么?
有两个派别: 和logical constness

  • bitwise constness 派认为:成员函数只有不改变对象的任何成员变量时才被称为const。这种想法有时并不合理,因为假设定义了一个类,其中有一个指针。const函数改变指针指向时是不合法的,但是如果我们改变了指针所指向的内容,此时实际上可以说改变了一个const对象,但是编译器却识别不出来。

  • logical constness 派认为:成员函数可以改变对象的某些bits,但是只有在客户端检测不出来的时候才可以这样做。那么const函数可以改变哪些变量呢?以mutable修饰的变量。改变指针指向内容实际上编译器也识别不出来,但是客户端能检测出来,因此这不叫const函数。

编译器执行的是bitwise constness ,但是我们在编程时,应该使用logical constness。

(4)避免同签名 const成员函数和non-const成员函数代码重复
有些时候,const函数和non-const的代码一样,但是一个为const函数,一个为non-const函数。如果这时分别写两个函数的话,显然造成了代码重复问题。

这时我们应该考虑实现函数一次,但是使用它两次。也就是说,必须令其中一个调用另一个。一般使用non-const 调用const版本来避免代码重复问题,并且在充当non-const使用时,应该将函数转换为const的。不能使用const来调用non-const来解决上述问题。

posted @ 2019-12-04 19:25  江南又一春  阅读(107)  评论(0编辑  收藏  举报