先看一段引用[1]:
许多人漠视一件事实:两个成员函数如果只是常量性(constess)不同,可以被重载
ok,下面为了解释,定义一下下面这个类:
class Cstring
{
private:
char str[32];
public:
Cstring(char instr[], size_t n)
{
for(int i=0;i<n;++i)
str[i] = instr[i];
}
char& operator [] (size_t i)
{
return str[i];
}
const char& operator [] (size_t i) const //注意这个const 常量,属于函数签名,因此导致重载
{
return str[i];
}
};
然后有以下应用:
char str[] = "hello world!";
Cstring cstr(str, sizeof(str)/sizeof(char));
const Cstring const_cstr(str, sizeof(str)/sizeof(char));
cout<<cstr[3]<<endl; //非const operator 版本
cout<<const_cstr[3]<<endl; //const operator版本
cstr[3] = '1';//ok
const_cstr[3] = '1';//error ,不能通过编译
然而,如果注释掉const operator 的版本,在GCC编译器中,给以下错误:
error: passing ‘const Cstring’ as ‘this’ argument discards qualifiers [-fpermissive]
所以,基于对class 本身的设计需求,决定要不要写两个版本的operator。
然而对于上述的代码,const 版本与非const版本的代码仅是相差了一个常量性的修饰,为了代码重用,可以做出以下更改:
class Cstring
{
private:
char str[32];
public:
Cstring(char instr[], size_t n)
{
for(int i=0;i<n;++i)
str[i] = instr[i];
}
char& operator [] (size_t i)
{
return const_cast<char&>(
static_cast<const Cstring&>(*this)[i]
);
}
const char& operator [] (size_t i) const
{
return str[i];
}
};
注意到,这次我们让non-const 调用了const 版本的函数,但是在调用的时候,我们使用了转型的操作。
首先是为对象本身加上const 属性,这个使用static_cast
然后返回值去掉const属性,这个使用const_cast
虽然使用转型会一定程度上损失效率,但是代码重复也是一件恼人的事情。那么在两者之间如何抉择倒是视需求而定了。
在[1]中有句名言:
二八定律:程序80%的时间是由20%的代码贡献的
所以如何去提高程序的运行效率,去思考如何改善那20%的代码更为重要:)
参考资料
[1] Scott Meyers 著, 侯捷译. Effective C++ 中文版: 改善程序技术与设计思维的 55 个有效做法[M]. 电子工业出版社, 2011. (条款03:尽可能使用const)