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)語法很相似?