运算符重载:
什么是运算符重载:
重载的运算符是特殊名字的函数,名字由operator关键字加上要重载的运算符组成,如operator+,和其他函数一样,重载的运算符也包含返回类型、参数列表和函数体。
运算符重载的使用细节:
(1)当一个运算符函数是成员函数,this绑定到了左侧运算对象。成员运算符函数的(显式)参数数量比运算对象的数量少一个。
(2)除了重载的函数调用运算符operator()之外,其他重载运算符不能含有默认实参。
(3)只能重载现有的运算符,无权发明新的运算符。
(4)运算符的重载不改变运算符优先级。
可以被重载的运算符:
+ - * / % ^ & | ~ ! , = < > <= >= ++ -- << >> == != && || ++ -= /= %= &= ^= |=
*= <<= >>= [] () -> ->* new new[] delete delete[]
不可以重载的运算符:
:: .* . ?:
调用重载运算符:
data1 + data2; //普通表达式
operator+(data1,data2); //等价的函数调用
//这两条语句式等价的,都调用了非成员函数operator+,传入data1和data2作为两个参数
data3 += data4 //基于调用的表达式
data3.operator+=(data2); //对成员运算符的等价调用
//这两条语句都调用了成员函数 operator+= ,将this绑定到data3的地址,将data4作为实参传入了函数
重载运算符作为成员函数和非成员函数的选择:
(1)单目运算符应该作为成员函数(不是必须)
(2)= () [] -> ->*必须是成员函数
(3)复合赋值运算符应该作为成员函数
(4)所有其他不是这些二元的运算符应该作为非成员函数
赋值运算符重载标准格式:
T& T::operator=(const T& rhs) {
if (this != &rhs) //检测自身不等于要赋值的对象
{
//perform assignment
}
return *this;
}
示例代码:
class Integer {
public :
Integer(int n = 0):i(n){}
const Integer operator+(const Integer& n) const {
return Integer(i + n.i);
}
int print() { return i; }
private:
int i;
};
int main()
{
Integer x(5), y(10), z1,z2,z3;
z1 = x + y; //等同于 z = x.operator+(y)
z2 = x + 3; //Integer类内构造函数被调用Integer(3):i(3){},后z = x.operator+(3)
//z3 = 3 + y; //编译不通过,3为第一个参数所以调用int型+,y不为int型且Integer类内没有方法把y变为int型参数
cout <<"Z1:"<< z1.print() << "\tZ2:" << z2.print() << endl;
return 0;
}
类型转换:
什么是类型转换:
类型转换运算符是类的一种特殊成员函数,它负责将一个类类型的之转换成其他类型。
标准格式:operator type() const; //type表示某种类型
类型转换运算符与构造函数:
类型转换和构造函数都能作T => C的类类型转换
(1)T(C) //构造函数
(2)operator T() const; //C中的类型转换
class Orange;
class Apple {
public:
operator Orange() const {} //类型转换
};
class Orange {
public:
Orange(Apple) {} //构造函数
};
void func(Orange){} //func函数参数为Orange
int main()
{
Apple ap;
func(ap); //编译不通过,报错:应用多个用户定义的从Apple到Orange的转换
return 0;
}
但是当构造函数与类型转换同时存在时,编译器无法知晓应使用哪种函数来进行转换,导致错误。
解决方法:(1)去掉其中一个函数。
(2)在构造函数前加explicit关键字,表示此构造函数只能用于构造,不能用于自动类型转换
小结:
应避免过度使用类型转换函数,因其容易形成误导性,所以要防止意外的事情发生。
一般使用一个专门的函数来调用进行类型转换,使程序更加一目了然发生了什么。