条款03:尽可能使用const

Use const whenever possible

面对指针,你可以指出指针自身、指针所指物,或两者都(或都不)是const;如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。

char greeting[] = "Hello";
char *p = greeting; //非常量
const char *p = greeting; //p指向的内容为常量,表示不能通过p修改p指向的内容,但p可以被修改
char * const p = greeting; //p指针本身为常量,但是可以修改p指向的内容
const char * const p = greeting; //p指针本身是常量,p指向的内容也为常量,不能修改p,也不能修改p指向的内容

STL迭代器系以指针为根据塑模出来,所以迭代器的所有就像个T*指针。声明迭代器为const就像声明指针为const一样。如果你希望迭代器所指的东西不可被改变,你需要的是const_iterator。

令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性。例如,考虑有理数的operator *声明式:

class Rational {...};
const Rational operator* (const Rational& lhs, const Rational& rhs);

如果不返回一个const对象,那么客户就能实现这样的暴行:

Rational a, b, c;
...
(a * b) = c;

如果a和b都是内置类型,这样的代码直截了当就是不合法。而一个“良好的用户自定义类型”的特征是它们避免无端地与内置类型不兼容,因此允许对两值乘积做赋值动作也就没什么意思了。将operator*的回传声明为const可以防止那个“没意思的赋值动作”,这就是该这么做的原因。

const成员函数

将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上。这一类成员函数之所以重要,基于两个理由。第一,它们使class接口比较容易被理解。这时因为,得知哪个函数可以改动对象内容而哪个函数不行,很少重要。第二,它们使“操作const对象”成为可能。这对编写高效代码是个关键,因为改善C++程序效率的一个根本办法是以pass by reference-to-const方式传递对象,而此技术可行的前提是,我们有const成员函数可用来处理取得(并经修饰而成)的const对象。许多人漠视一件事实:两个成员函数如果只是常量性不同,可以被重载。这实在是一个重要的C++特性。

class TextBlock{
public:
	const char &operator[](std::size_t position) const //for const对象
	{
		return text[position];
	}
	char &operator[](std::size_t position) //for non-const 对象
	{
		return text[position];
	}
private:
	std::string text;
};

TextBlock tb("Hello");
std::cout << tb[0]; //调用non-const

const TextBlock ctb("World");
std::cout << ctb[0]; //调用const

让我们为哲学思辩喊一次暂停。成员函数如果是const以为什么?这有两个流行概念:bitwise constness(又称physical constness)和logical constness。

bitwise const阵营的人相信,成员函数只有在不更改对象之任何成员变量时(static除外)时才可以说是const。也就是说它不更改对象内的任何一个bit。这种论点的好处是很容易侦测违反点:编译器只需要寻找成员变量的赋值动作即可。bitwise constness正是C++对常量性的定义,因此const成员函数不可以更改对象内任何non-static成员变量。

不幸的是许多成员函数虽然不十足具备const性质却能通过bitwise测试。更具体的说,一个更改了“指针所指物”的成员函数虽然不能算是const,但如果只有指针(而非其所指物)隶属于对象,那么称此函数为bitwise const不会引发编译器异议。

而logical constness这一派的主张,一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才得如此。而实现这一功能是利用C++的一个与const相关的摆动场:mutable(可变的)。mutable释放掉non-static成员变量的bitwise constness约束。

在const和non-const成员函数中避免重复

避免两个版本的operator[]中出现重复代码,你应该做的是实现operator[]一次并使用它两次。也就是说,你必须令其中一个调用另一个。这促使我们将常量性移除。

class TextBlock{
	...
	const char & operator[](std::size_t position) const
	{
		...
		...
		...
		return text[position];
	}
	char & operator[](std::size_t position)
	{
		return const_cast <char &> (
			static_case<const TextBlock&>(*this)[position]
			);
	}
};

本例中const operator[]完全做掉了non-const版本该做的一切,唯一不同是其返回类型多了一个const修饰。这种情况下如果将返回值deconst转除是安全的,因为不论谁调用non-const operator[]都一定首先有个non-const对象,否则就不能够调用non-const函数。反向做法,令const版本调用non-const版本以避免重复,并不是你该做的事。记住,const成员函数承诺绝不改变其对象的逻辑状态,non-const成员函数却没有这般承诺。如果在const函数内调用non-const函数,就是冒了这样的风险:你曾经承诺不改动的那个对象被改动了。

请记住

  • 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
  • 编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness)。
  • 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值