条款03:尽可能使用const

关键字const多才多艺。你可以用它在classes外部修饰global或namespace作用域中的常量,或修饰文件、函数或区块作用域中被声明static的对象。你也可以修饰classes内部的static和non-static成员变量。面对指针你也可以指出自身、指针所指物或两者都(或不)是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 p = greeting;//const pointer,const data

规则:如果const出现在星号的左边,表示被指物是常量;如果const出现在星号右边,表示指针本身是常量。

STL迭代器系以指针为根据模塑的,所以迭代器的作用就像一个T*指针。声明迭代器为const就像声明指针为const一样(即声明一个T* const指针),表示指针本身为const;如果你希望迭代器所指向的内容不被改变(即希望const T*指针),你就需要一个const_iterator:

std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin(); //iter就像一个T* const
*iter = 10;			//ok
++iter; 			//错误

std::vector<int>::const_iterator cIter = vec.begin(); //cIter就像一个const T*
*cIter = 10;		//错误
++iter;	

const最具威力的用法是面对函数声明时的应用。在一个函数声明里内,const可以用于函数返回值、各参数、函数自身。

令函数返回一个常量,可以防止客户错误的操作临时变量,又不至于放弃安全性和效率。

const成员函数

将const用于成员函数的目的:第一,为了确认哪些该成员函数改动对象内容哪些函数不行;第二,它们使操作const对象成为可能,编写高效率的代码关键是pass by reference-to-const方式传递对象。

对于两个成员函数如果只有常量性不同,可以被重载。

成员函数如果是constI意味着两个流行概念:bitwise constness(又称physical constness)和logical constness。

bitwise const

成员函数只有在不更改对象的的任何成员变量(static除外)才可以说是const。不幸的是许多成员函数虽然不十足具备const性质却能通过bitwise测试,具体的说:一个更改了“指针所指物”的成员函数不能算是const,但如果只有指针(而非其所指物)属于对象,那么称此函数为bitwise const通过编译。假如如下一个TextBlock class:

class TextBlock
{
public:
	...
	char& operator[](std::size_t position) const //bitwise const声明,但其实不适当
	{
		return m_text[position];
	}
private:
	char*  pText;
};

这个class的operator[]声明为const,但是返回却是reference。

const TextBlock ctb("Hello"); //声明const对象
char* pc = &ctb[0];            //调用const operator[]取得一个指针,指向ctb的数据
*pc = 'J';                    //ctb现在变成了"Jello"

这代码没有错:你创建了一个常量对象并设定某值,而且只调用了const成员函数,但你终究还是改变了它的值。

logical constness

这派主张:一个const成员函数可以修改它所处理的对象某些bits,只有在客户端侦测不出来的情况下可以如此。

例如你有个CTextBlock class有可能高速缓存文本区块的长度以便应付询问:

class CTextBlock
{
public:
	//...
	std::size_t length() const;
private:
	char* pText;
	std::size_t textLength; //最近一次计算的文本区块长度
	bool lengthIsValid;		//目前是否有效
};

std::size_t CTextBlock::length() const
{
	if(!lengthIsValid)
	{
		textLength = std::strlen(pText);	//错误,在const成员函数中不能
		lengthIsValid = true;				//赋值给textLength和lengthIsValid
	}
		
	return textLength;
}

length的实现不是bitwise constness,因为textLenghth和lengthIsValid都被改变。解决的方法是:将变量加上mutable修饰符。

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

对于bitwise-constness不是我们想要的,可以用mutable解决,但它不能解决所有的const相关的难题。对于重载的的operator[]操作符不止单纯的返回一个reference指向某个字符,甚至执行边界检查、日志访问信息,还有其他内容等等。把所有的同时放入const和non-const operator[]中,导致代码重复,编译时间、维护和代码膨胀。

虽然类型转换是一个糟糕的方法,然而代码重复也不会令人愉快。我们可以用non-const operator[]直接复用const operator[]的代码

	const char& operator[](std::size_t position) const
	{
		//...边界检查
		//...日志访问
		//...检查数据完整性
		return m_text[position];
	}
	char& operator[](std::size_t position)
	{
		return const_cast<char&>								//将op[]返回值的const移除
			(static_cast<const TextBlock&>(*this)[position]);	//为*this加上const调用const op[]
	}

上述使用了两次转换动作:第一次用*this添加const(用于调用const operator[]版本,否则调用自身),第二次从const operator[]返回中移除const。

总结:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值