c++有时候需要为类的某个成员函数重载常量与非常量的版本,定义常量版本是为了保证该函数可作用于常量类对象上,并防止函数改动对象内容。但有时两个版本的函数仅仅是在返回的类型不同,而在返回前做了大量相同的工作,那么代码会有大量重复,由此也会带来编译时间和代码膨胀等开销。例如下面的类成员函数:
1 #include 2 3 class Test 4 { 5 using size_type = std::string::size_type; 6 public: 7 const char & operator[](size_type index) const; 8 char & operator[](size_type index); 9 private: 10 std::string str; 11 }; 12 13 const char & Test::operator[](size_type index) const 14 { 15 //... 16 //... 17 //要执行的操作 18 return str[index]; 19 } 20 21 char & Test::operator[](size_type index) 22 { 23 //... 24 //... 25 //要执行的操作 26 return str[index]; 27 }
虽然完全可以将执行的相同的动作写在一个新的成员函数里并将其定义为私有的,但这样仍然有函数调用和返回值的重复。一种可行的方法是让两个函数的其中一个调用另一个,如下所示:
1 class Test 2 { 3 using size_type = std::string::size_type; 4 public: 5 const char & operator[](size_type index) const; 6 char & operator[](size_type index); 7 private: 8 std::string str; 9 }; 10 11 const char & Test::operator[](size_type index) const 12 { 13 //... 14 //... 15 //要执行的操作 16 return str[index]; 17 } 18 19 char & Test::operator[](size_type index) 20 { 21 return const_cast(static_cast(*this)[index]); 22 }
非常量版本调用了常量版本,在这过程中进行了两次转型:
①static_cast(*this),将非常量类型的Test对象转换成常量类型(const)的Test对象,即把类型的*this转换成类型,再调用常量版本的下标运算符。若没有进行转换直接调用下标运算符,等于函数递归调用自己,将会进入死循环。
②const_cast(),上一步的static_cast(*this)[index]调用了常量版本的下标运算符,返回的类型为const char &,接下来便利用const_cast去除返回值的const属性。
附注1:
应该注意函数调用的主宾关系:非常量版本调用常量版本并进行类型转换。反过来的做法则是一种错误的行为,常量版本成员函数不会改变对象的逻辑状态,而在常量版本中调用非常量版本则存在着可能改变对象的风险。实际上这样的代码若要通过编译,需要用const_cast去除*this的const属性,这显然是一种不安全的行为。相反,非常量版本成员函数本来就可以改动对象,因为在函数内部调用常量版本并不会有风险。