C++中运算符的重载
有4个操作符是不能重载的: :: 。 .* ?:
1:重载赋值(=)运算符
一般我把重载赋值运算符定义为类成员函数(基本上着也是必须得),如果你没有定义自己的重载赋值运算符的话,编译器就会自动生成一个,完成类对象之间逐值的复制。关于在两个类对象赋值 A a,b; a=b;
我们对这个重载函数的形参有以下三种形式:
operator=(A);
operator=(A&);
operator=(const A&);
以上三种形参都是可以的,但是在标准C++中,后两种的定义可能会出现问题啊,所以还是推荐第一种方式(虽然多调用了一次复制构造函数)
关于这个重载函数的返回值,我们应当定义为对类的引用,下面是对这个函数的完整定义
A& A::operator(A a)
{
/* 实现对象的复制 */
return (*this);
}
2:输入和输出操作符
输出操作符<<的重载:为了与IO标准库一直,操作符应该接受一个ostream&座位第一个形参(因为IO对象不允许复制或赋值),对类类型const对象的引用座位第二个形参(非const引用和单纯类对象也行,但这地方对类对象没进行改变,所以还是推荐const A&形参),返回值是ostream&
即 ostream& operator<<(ostream& os,const A& a);
输入操作符>>的重载
输入操作符和输出操作负形参区别的地方就是它的第二个参数不能是const类型的
即 istream& operator>>(istream& is,A& a);
最后,最重要的一点是,重载<<,>>操作符函数必须不能是非成员函数,因为这个函数的做操作数是io对象,而不是类对象
3:重载下标[ ]操作符
重载的下标操作符函数必须声明为类成员函数
定义下标操作符时,一般要定义两个版本:一个吻非const成员函数并返回引用,另一个为const成员函数并返回const引用
4:重载*和->操作符
这两个操作符重载函数我们一般都应该(基本上也必须)定义位类成员函数
*操作符是一元操作符,它的重载函数没有形参,返回值类型根据实际确定
->操作符与众不同,它可能表现得像二元操作符一样:接受一个对象和一个成员名。对对象解引用以获取成员。不管外表如何,->操作符不接受显示形参
这里没有第二个形参,因为->的右操作数不是表达式,而是对应着类成员的一个标识符,没有明显可行的途径将一个标识符作为形参传递给函数,相反,由编译器处理获取成员的工作(解释的很拗口,后面具体说明)
加入我们定义一个类 class A{ /* 定义*/};
当这样编写时 point->action();
1:当point是类A的一个对象指针时(即A *point=new A),这时编译器就认为上面的意思是,调用point类所指对象的 action()成员(成员函数),这时是没有用到重载操作符->的
2:当point是类A的一个类对象时(即 A point;) ,point->action()可以这样理解 (point.operator->())->action(), 这时才是调用的重载->函数
由此可看出,我们的->重载函数的返回值必须是指针类型
5:重载自增++/自减--操作符
自增/自减有两种形式:前缀和后缀,这两种重载函数的区别就是:前缀的重载函数形参为空,而后缀的重载函数的形参有一个int类型值
前缀自增/自减操作符的返回值应该是类类型的引用,即 A&,(return (*this)),因为这是类对象的状态发生了变化,但我们要获得的仍然是变化后的值,所以应该是返回引用
后缀自增/自减操作符应该返回的是类对象 即A ,因为类对象也发生了变化,但要返回的是变化之前的值
总结一下:若类对象在操作符函数中发生了变化,并且要返回变化后的值时,这是应该返回类对象引用;若不是要返回变化后的值时,要返回类对象
A& operator++() //前缀
{//-------
return (*this);
}
A operator++(int)
{
A a(*this);
//---
return a;
}
6:调用操作符和函数对象
调用操作符的定义:
返回值 operator() (形参)
{
return ……;
}
函数调用操作符重载函数和构造函数调用的区别是: 当你定义并初始化一个类对象时调用的是构造函数,其余时候调用的都是调用操作符重载函数
定义了调用操作符的类,其类对象常称为函数对象,即它们是行为类似函数的对象( 即可实现 a()调用)
7:转换操作符和类对象
首先我们介绍explicit关键字 :
假如类的构造函数只有一个形参,那么在需要类对象的地方用该形参对象去代替,则会产生隐式的类型转换,生成临时对象
下面是例子说明:
class A{
public:
A(int i){}
};
假如说我们现在有一个函数是这样的 void f(A)
那么我们调用这个函数的时候这样调用f(100(一个int型值))是不会产生变异错误的,因为这时发生的就是隐式类型转换,
int->A类型,
所以我们的关键字explicit的作用就是 ,阻止这种隐式转换
explicit A(int){} 那么我们调用f(100)是就会出现编译错误,隐式转换不会自动发生
下面是转换操作符
类转换操作符的定义也很简单:谨记以下规则
1:该转换操作符重载函数没有返回类型
2:这个函数的形参列表必须为空(带默认形参也不行,必须为空)
3:这个函数名就是类类型要转换的类型名
4:一般我们把这个函数声明为cons函数
5:在函数体里应该返回和函数名类型对应的值
下面是例子,假设类型A可以转换成int型,那么类A的定义如下
class A
{
public:
operator int() const
{
return 42; //return 一个int值
}
};
只能应用一个类类型转换:
还需谨记的一点就是,这种类型转换不支持传递,即假如A类型可以转换成B类型,B类型可以转换成C类型,那么A类型就不能明显的转换成C类型