一、 const 和成员函数的故事
const 的用途有以下几种:
- 修饰全局、局部、成员变量
- 修饰成员函数
修饰变量的时候 const 限制了变量在整个程序运行期间都是不能修改的,而修饰成员函数的时候限制函数内不能修改数据成员,这应该是所有 C++程序员都烂熟于心的准则。但除了这两条准则以外,const 还有一些隐含的准则,是写代码的时候是很容易被忽视的。例如以下代码,相信很多人都无法一眼看出这段代码的问题在哪:
其实问题很简单:PrintHeroName
函数的形参是一个 const 类型的 Hero 对象,但是在函数体内,这个 const 类型的 Hero 对象调用了一个非 const 的成员函数,这是不允许的。这种情况下,编译器会报错:
如果想要在 PrintHeroName
函数中调用 h.GetName
方法,只需要把形参里的 const 去掉就可以了。或者把 GetName
修改成一个 const 属性的成员函数。
其实最开始我也没有发现这段代码的问题,作为亲手写下这段代码的始作俑者,我甚至还捉摸了一段时间。但这其实就是一个简单的用法,因为使用得过于习惯,并且自认为非常熟悉,所以就忽视了问题所在。
二、为什么要使用 const_cast
const_cast 的作用是去掉变量的 const 或者 volatile 限定符——这看起来很鸡肋,因为很多人都在想:我既然要去掉 const 限制,那我直接不使用 const 修饰不就完事了吗?何必多此一举?其实最开始我也是这么认为的,所以从来没有用过这个关键字。直到今天遇到上面的那个问题之后,才突然明白 const_cast 的真正用途。
上面的程序,把 PrintHeroName 形参的 const 去掉后,程序可以正常编译了。问题是,如果调用它的上层函数,Hero 对象已经被 const 限定了,应该怎么办?例如:
可以看到,CreateHero
的形参是 const 的,但 PrintHeroName
是非 const 的,无法在 CreateHero
函数中将 h 传递到 PrintHeroName
,因为不能直接将一个 const 对象转换成非 const 的。程序编译也会报错:
这种情况下就要用到 const_cast 了,这里才是 const_cast 大展拳脚的地方:因为我们很明确能知道 GetName() 是不会修改任何成员对象的值的,所以可以在这里通过 const_cast 去掉 Hero 的 const 限定,使得程序可以正常往下调用。即,只要把 h 通过 const_cast 包裹起来就可以了:
1F
修改为
void PrintHeroName(const Hero &h)
应该是一个更好的方案