重载运算符的定义方式通常为(以+运算符举例)type operator+(argu1, argu2,..) {}
1.如果一个重载的运算符是成员函数,则它的第一个(左侧)运算对象绑定到隐式的this指针上。成员运算符函数的(显式)参数数量比运算对象的数量少一个。 运算符的调用通常是调用重载的运算符函数,所以可以像调用普通函数一样直接调用运算符函数。eg:
1 2 3 4 5 6 7 data1 + data2; operator +(data1, data2); data1 += data2; data1.operator +=(data2);
2.运算符重载时,具有关联的运算符应该通过调用进行重载。例如在定义==
和!=
时,可以先定义==
,然后定义!=
时调用==
的重载运算符。
3.赋值(=)、下标([])、调用(())和成员访问箭头(->)运算符必须是成员函数。复合赋值运算符通常应该是成员,改变对象状态的运算符如递增、递减和解引用通常也应该是成员。而具有对称性的运算符有可能在调用时需要转换任意一端的运算对象,如算术、相等性、关系和位运算等通常应该为非成员函数。
4.不能对两个基本的内置变量进行运算符重载,如int operator+(int, int)
是错误的。
输入(>>)输出(<<)运算符 5.重载输出运算符时应尽可以的减少对格式控制的操作。输入输出运算符必须是非成员函数,且IO对象必须是引用,通常返回也是引用,输出对象应该是const引用,因为不应该修改输出对象。输入运算符还应该检查并处理可能的输入出错情况,如有效性。eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ostream &operator <<(ostream &os, const Sales_data &item) { os << item.isbn () << " " << item.units_sold << item.price (); return os; } istream &operator >>(istream &is, Sales_data &item) { double price; is >> item.bookNo >> item.units_sold >> price; if (is) item.revenue = item.units_sold * price; else item = Sales_data (); }
关系运算符 关系运算符通常应该定义一个同时也定义关联和其他运算符,如定义了<
,应该也定义>
等 6.相等和不等运算符举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 bool operator ==(const Sales_data &item1, const Sales_data &item2){ return item1.isbn () == item2.isbn () && item1.units_sold == item2.units_sold && item1.revenue == item2.revenue; } bool operator !=(const Sales_data &item1, const Sales_data &item2){ return !(item1 == item2); }
7.赋值运算=的重载方式与拷贝赋值运算符类似,定义时也必须先释放当前的内存空间,再创建新的空间,以解决自赋值的问题,且必须是成员函数。
下标运算符 如果一个类定义了下标运算符,则它通常应该有两版本:一个返回普通引用,另一个是类的常量成员并且返回常量引用。eg:
1 2 3 4 5 6 7 8 9 class strVec {public : std::string& operator [](std::size_t n) { return elements[n]; } const std::string& operator [](std::size_t n) const { return elements[n]; } private : std::string *elements; };
递增递减运算符 定义递增和递减运算符的类应该同时定义其前置和后置版本,operator++()
默认情况下为前置版本(++obj),区分后置版本则需要额外提供一个不被使用的int类型的形参,编译器会为该形参提供一个值为0的实参。递增递减应该检查索引值的有效性,当定义了前置版本之后,就可以利用前置版本来完成后置版本的实际工作。eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class StrBolbptr {public : StrBlobPtr& operator ++(); StrBlobPtr& operator --(); StrBlobPtr& operator ++(int ); StrBlobPtr& operator --(int ); } StrBlobPtr& StrBlobPtr::operator ++() { check (curr, "increment past end of StrBolbPtr" ); ++curr; return *this ; } StrBlobPtr& StrBlobPtr::operator --() { --curr; check (curr, "decrment past begin of StrBlobPtr" ); return *this ; } StrBlobPtr& StrBlobPtr::operator ++(int ) { StrBlobPtr ret = *this ; ++*this ; return ret; } StrBlobPtr& StrBlobPtr::operator --(int ) { StrBlobPtr ret = *this ; --*this ; return ret; }
显式调用前置和后置递增运算符的方式:
1 2 3 StrBlobPtr p (a1) ; p.operator ++(0 ); p.operator ++();
重载成员访问运算符*和-> 箭头运算符必须是类的成员,解引用运算符通常也是类的成员。可以先定义解引用运算符,然后箭头运算符由解引用来完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class StrBlobPtr {public : std::string& operator *() const { auto p = check (curr, "dereference past end" ); return (*p)[curr]; } std::string* operator ->() const { return & this ->operator *(); } }
函数调用运算符() 如果类定义了调用运算符,则该类的对象就称作函数对象。一个类可以定义多个函数调用运算符,只需要在参数数量和类型上有区别。eg:
1 2 3 4 5 6 7 struct absInt { int operator () (int val) const { return val < 0 ? -val : val; } };
调用:
1 2 3 int i = -42 ;absInt absobj; int ui = absobj (i);
如果函数对象类中含有一些数据成员,且这些数据成员被用于定制函数调用运算中的操作,这样的类称为含有状态的函数对象类,也就是说这种类的函数对象可以带有状态。eg:
1 2 3 4 5 6 7 8 9 class PrintString {public : PrintString (ostream &o = cout, char c = ' ' ) : os (o), sep (c) {} void operator () (const string &s) const { os << s << sep; } private : ostream &os; char sep; };
调用:
1 2 3 4 PrintString printer; printer (s); PrintString errors (cerr, '\n' ) ; errors (s);
lambda是函数对象 lambda表达式实际是一个含有重载函数调用运算符的类,该类只含有重载的函数调用运算符,不含默认构造函数、赋值运算符及默认析构函数。eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 auto wc = find_if (words.begin (), words.end (), [sz](const string &s) { return s.size () >= sz; })class SizeCmp { SizeCmp (size_t n) : sz (n) {} bool operator ()(const string &s) const { return s.size () >= sz; } private : size_t sz; }; auto wc = find_if (words.begin (), words.end (), SizeCmp (sz));上述调用可改写为:
标准库定义的函数对象 标准库的头文件functional中定义了一组表示算术运算符、关系运算符和逻辑运算符的类,每个类分别定义了一个执行命命操作的调用运算符。例如plus<Type>
类定义了一个函数调用运算符用于对一对运算对象执行+操作。还有minus、equal_to、greater等。用法:
1 2 3 4 5 plus<int > intAdd; int sum = intAdd (10 , 20 );sort (svec.begin (), svec.end (), greater <string>());
对两个无关指针的比较将产生未定义的行为,但标准库的函数对象也可以用于对指针进行比较。eg:
1 sort (nameTab.begin (), nameTab.end (), less <string*>());
标准库的function类型 C++11中新增的function是一个模板类,利用该模板,可以将不同类型但具有相同调用形式的函数放到同一个函数指针变量中。例如定义接受两个int参数,返回一个int参数的计算函数(加,减,乘,除),声明为function<int(int, int)> f = add;
,此时f可以指向任意符合接受两个int返回一个int的函数。 C++中的可调用对象有:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。 function举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> #include <string> #include <map> #include <functional> int add (int i, int j) { return i + j; } auto mod = [](int i, int j){ return i % j; }; struct Div { int operator () (int i, int j) const { return i / j; } }; auto binops = std::map<std::string, std::function<int (int , int )>>{ { "+" , add }, { "-" , std::minus <int >() }, { "/" , Div () }, { "*" , [](int i, int j) { return i*j; } }, { "%" , mod } }; int main () { while ( std::cout << "Pls enter as: num operator num :\n" , true ) { int lhs, rhs; std::string op; std::cin >> lhs >> op >> rhs; std::cout << binops[op](lhs, rhs) << std::endl; } return 0 ; }
当使用重载的函数与function时,应该使用函数指针,以免由于同名函数名引起二义性。eg:
1 2 3 4 5 6 7 8 9 10 int add (int i, int j) {return i + j}Sales_data add (const Sales_data&, const Sales_data&); map<string, function<int (int , int )>> binops; binops.insert ({"+" , add}); int (*fp)(int , int ) = add; binops.insert ({"+" , fp}); binops.insert ({"+" , [](int a, int b){ return add (a, b); } });
类型转换运算符 类型转换运算符是类的一种特殊成员函数,它负责将一个类类型的值转换成其他类型。形如:operator type() const;
类型转换运算符可以面向任意类型(void除外)进行定义,只要该类型能作为函数的返回类型,所以不允许转换成数组或者函数类型。但允许转换成指针(如数组指针和函数指针)或者引用类型。 一个类型转换函数必须是类的成员函数,它不能声明返回类型,形参列表也必须为空,类型转换函数通常应该是const。eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class SmallInt {public : SmallInt (int i = 0 ) : val (i) { if (i < 0 || i > 255 ) throw std::out_of_range ("Bad SmallInt value" ); } operator int () const {return val;} int operator int () const ; operator int (int = 0 ) const ; operator int *() const { return 42 ; } private : std::size_t val; }
该SmallInt类即定义了向类类型的转换,也定义了从类类型向其他类型的转换。
1 2 3 SmallInt si; si = 4 ; si + 3 ;
C++11中引入了显式的类型转换运算符(explicit conversion operator):
1 2 3 4 5 6 7 8 class SmallInt {public : explicit operator int () const { return val; } } SmallInt si = 3 ; si + 3 ; static_cast <int >(si) + 3 ;