C++中的operator主要有两个作用,一是操作符的重载,一是自定义对象类型的隐式转换。对于操作符的重载,许多人都不陌生,但是估计不少人都不太熟悉operator的第二种用法,即自定义对象类型的隐式转换,我们下面就用以下这个小例子温故一下这两种用法:
1 #include <iostream> 2 #include <sstream> 3 using namespace std; 4 5 class FuncObj 6 { 7 public: 8 FuncObj(int n): _n(n) { 9 cout << "constructor" << endl; 10 } 11 12 bool operator()(int v) { 13 cout << "operator overload" << endl; 14 return v > _n; 15 } 16 17 operator string() { 18 cout << "type convert" << endl; 19 stringstream sstr; 20 sstr << _n; 21 return sstr.str(); 22 } 23 24 int _n; 25 }; 26 27 int main() 28 { 29 FuncObj obj(10); 30 if (obj(11)) 31 cout << "11 greater than 10" << endl; 32 33 string str(obj); 34 cout << str << endl; 35 }
第12行是操作符重载,重载()使得该对象成为一个函数对象,即该对象有类似函数的功能,在很多场合下可以当成函数指针使用,在STL的很多算法模板里广泛使用。FuncObj用过操作符重载可以判断传入的参数是否大于一个预先设定好的值(在构造函数里指定),见代码的29~31行。
17行的定义表名FuncObj对象可以隐身转换成string,这就是operator的第二个用法,使用方法见代码的33~34行。注意在函数声明时,operator关键词出现在返回类型的前面,区别与操作符重载时的用法。
上述代码的输出:
constructor operator overload 11 greater than 10 type convert 10
顺便说点题外话,第33行把FuncObj类型的对象传入string的构造函数,是用了c++构造函数的隐式类型转换特性,虽然string类并没有显式定义参数为FuncObj的构造函数,但因为其可以隐式转换为string,所以语法上都是合法的。构造函数的隐式类型转换,是使用一个其他的类型构造当前类的临时对象并用此临时对象来构造当前对象,这种转换必须有构造函数的支持;operator算子的隐式类型转换,使用当前对象去生成另一个类型的对象(正好与构造函数隐式转换相反),这种转换必须有operator算子的支持。当然了,构造函数的隐式类型转换有利有弊,类的设计者就起决定性作用了,如果你不想让构造函数发生隐式的类型转换,请在构造函数前加explicit关键字;同时,operator算子声明的隐式类型转换也可以通过一些相应的返回值函数替代,用户的掌控性更好。
最后,用过实现一个经常发生的普遍需求(string转其他基本数据类型)让读者加深一下,operator自定义对象类型的隐式转换功能的用法。
1 template <typename T> 2 class string_cast 3 { 4 public: 5 string_cast(const std::string &from): m_from(from) { 6 } 7 operator T() const { 8 std::stringstream sstr(m_from); 9 T ret; 10 try { 11 sstr >> ret; 12 } 13 catch(std::exception &e) 14 { 15 return T(0); 16 } 17 return ret; 18 } 19 private: 20 const std::string &m_from; 21 };
string转int的用法:
cout << string_cast<int>("12345") << endl;
string转double的用法:
cout << string_cast<double>("12345.78") << endl;
是不是和c++的其他类型转换(static_cast,const_cast,dynamic_cast, reinterpret_cast)语法很相似?