C++守则——尽可能使用const

const不管放在哪都是 表示此值不可被修改,此外还需要区别的就是const是修饰指针还是修饰数值:在*号前是指针所指的数值不可被修改,称为底层const,*号后面表示是指针不可被修改,称为顶层const。我们看的是const是在*的左边还是右边,与其在变量类型的那边并没有多大的关系,完全看个人习惯,例如下面两行代码是一样的,都是指针所指的数值不可改变:

const int *p;
int const *p;

const 最具威力的用法是面对函数声明式的应用。在一个函数声明内,const可以和函数返回值、各参数、函数自身(如果式成员函数)产生关联。这样就可以降低程序员因为错误而造成的以外而又不至于放弃安全性和高效性。举个例子,你是不是曾经也犯过这样的小错误(反正我浪费过时间找这种错误):

if(a * b = c)  ...    //本该是两个等于号

这样是不会报错的,但不一定会是你想要的结果,反而不是你想要的结果的可能性更高一些。那如果我们在重定义赋值操作符的时候加个const呢?我们来看看:

class Rational {...};   假设有这么一个类
const Rational operator* (const Rational &lhs, const Rational &rhs);

这样的话就可以避免上述情况了,或者说编译器在你因为手误而写成错误的断言的时候会给你报错,因为你试图将c的值赋给一个不可改变的值,一看, 哦,这里少写了一个等号。所以,const的特征是可以避免没意思的赋值
事实证明,当你不需要修改一个数时候,最好是将它声明成为一个const,这样可以省去很多不必要的而又恼人的错误,而且查找bug的速度也会快一点(例子见上面)。

此外,我们还将const用于成员函数,其目的在于确认该函数成员函数可作用于const对象身上。理由有二:
1、它们使得class接口比较容易理解。得知哪个函数可以改动对象内容二那个函数不行是一件很重要的事情。
2、他们使得操作const对象成为可能。
函数重载中有这么一条:两个成员函数如果只是常量不同,可以被重载。来看以下代码:

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

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

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

std::cout << tb[0];
tb[0] = 'x';
std::cout << ctb[0];
//ctb[0] = 'x';        错误,因为ctb是一个const,不可改变

代码中,non-const operator[]的返回类型是个引用,如果不是char,那么tb[0] = 'x’就不会通过编译。因为如果函数返回值是个内置类型,那么改动函数返回值从来就不合法。纵使合法,C++以by value返回对象 这一事实以为被改动的其实是tb.text[0]的一个副本,并不是它本身!
我们在上面的代码中定义了两个功能一样的operator[],对于程序员而言是不是枯燥了点?没事,我们玩点有挑战性的:用已经定义的const函数定义non-const函数,废话不多,上代码:

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

此代码中的non-const operator[]方法被强制类型转换了两次。先来看<const TextBlock&>。我们打算让它调用的是自己的兄弟:const operator[]函数的,但是我们必须明确指出我们是要调用const operator[]函数,添加const进返回值,于是乎,我们就将TextBlock&转型为const TextBlock&。事实上,如果不强制类型转换,我们的non-const operator[]会调用自身,递归到很多次,这么强制转换就可以避免自己的无穷递归。再来看第二个:<char&>。我们的值经过最里面的强制类型转化后得到的值是一个const值,可是我们函数返回值是一个non-const值,于是乎,我们就将const值再变回non-const,将const属性从返回值中移除。
至此,我们就做到了运用const成员函数实现其non-const兄弟。
那么我们可不可以反过来,用non-const成员定义其const兄弟呢?答案是不可以!因为const承诺不会改变其对象的逻辑状态,但是non-const并没有,如果再const内调用了non-const,就意味着你在不准改变的对象身上被改变了。

总结一下:
1、将某些东西声明为const可以有助于侦察出错误,const可以被施加于任何作用域内的对象,函数,函数返回值类型、成员函数本体。
2、当const和non-const成员函数有着实质等价的实现,零non-const版本调用const版本可避免重复

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值