运算符重载实质上就是函数重载,重载为成员函数,他就可以自由访问本类的数据成员。实际使用时,总是通过该类的某个对象来访问重载的运算符。
如果是双目运算符,左操作数是对象本身的数据,由this指针指出,右操作数则需要通过运算符重载函数的参数表来传递;如果是单目运算符,操作数由对象的this指针给出,就不再需要任何参数。
(1)双目运算符的重载
对于双目运算符B,如果要重载为类的成员函数,使之能够实现表达式a B c
,其中a1是A类的对象,则应当把B重载为A类的成员函数,该函数只有一个形参,形参类型为c所属的类型。经过重载之后,表达式a B c
就相当于调用a.operator B(c)
。
(2)单目运算符的重载
对于前置单目运算符U,如“-”(负号)等,如果要重载为类的成员函数,用来实现表达式U a
,其中a为A类对象,那么U应当重载为A类的成员函数,函数没有形参。经过重载之后,表达式U a
就相当于调用a.operator U()
对于后置运算符“++”和“–”来说,如果将它们重载为类的成员函数,用来实现表达式a++
或者a--
,其中a为A类对象,那么运算符就应当重载为A类的成员函数,这时函数要带有一个整型(int)形参。重载之后,表达式a++
和a--
就相当于调用a.operator++(0)
和a.operator--(0)
。这里的int类型的参数在运算中不起任何作用,只是用于区别后置++、–和前置++、–。
【例1】两个对象相加的实现体,即两个对象相加的原理。就是把两个对象中的数据成员依次进行相加。
class A
{
public:
A(int i = 0, int j = 0, int k = 0) :m_i(i), m_j(j), m_k(k) {}
int GetI()const
{
return m_i;
}
int GetJ()const
{
return m_j;
}
void Add(A& s)
{
cout <<"("<< this->m_i + s.m_i << "," << this->m_j + s.m_j << "," << this->m_k + s.m_k<<")"<<endl;
}
private:
int m_i;
int m_j;
int m_k;
};
void main()
{
A a(2, 5, 7);
A b(5, 7, 9);
a.Add(b); //a+b
}
运行结果:
【例2】两个对象相乘的实现体,即两个对象相乘的原理。就是把两个对象中的数据成员依次进行相乘。
class A
{
public:
A(int i = 0, int j = 0, int k = 0) :m_i(i), m_j(j), m_k(k) {}
int GetI()const
{
return m_i;
}
int GetJ()const
{
return m_j;
}
void fn(A& s)
{
cout << "(" << this->m_i * s.m_i << "," << this->m_j * s.m_j << "," << this->m_k * s.m_k << ")" << endl;
}
private:
int m_i;
int m_j;
int m_k;
};
void main()
{
A a(2, 5, 7);
A b(5, 7, 9);
a.fn(b); //a * b
}
运行结果:
以上两个个例子中,在执行Add函数a.Add(b);
或者fn函数a.fn(b);
之前,在执行结果还没有出来的情况下,我们是不知道这两个函数到底是干什么的,这导致程序的可读性不好。虽然上面两个例子中通过函数的调用实现了我们所需要实现的功能,但是这样的写法不如直接写成运算符可以直观的表达出我们要现实的功能是什么。所以引出运算符重载。
运算符重载的原因:
①想让当前的对象像基本数据类型去操作。
②把对象的运算写成运算符的形式比写成函数调用的形式可读性更强。
【例3】把运算符“+”重载为成员函数
class A
{
public:
A(int i = 0) :m_i(i) {}
A operator+(A& s)
{
cout << "调用函数operator +()" << endl;
return this->m_i + s.m_i;//把A类中的m_i和B类中的m_i相加,this指针指向当前对象a中的数据成员m_i的地址
//此处返回值为int型,是因为构造函数可以进行类型转换
}
void print()
{
cout << m_i << endl;
}
private:
int m_i;
};
int main()
{
A a(5);
A b(6);
//a + b;//相当于a.operator+(b) //+(a.b)
(a + b).print();/*遇到+,先看类中有没有重载加法运算符,如果重载了,去调用这个+的重载函数,
把第一个操作数a当作this指针传给这个重载函数,把右操作数b当作实参传给+的重载函数的形参*/
A c;
c = a.operator+(b);
c.print();
return 0;
}
运行结果:
结果分析:
在程序中,遇到“+”,先看此处的“+”是不是加法运算符重载,如果重载了,去调用这个“+”的重载函数,在上述程序,主函数中遇到的a+b
,这里的“+”是加法运算符重载,所以去调用A operator+(A& s)
重载函数,把第一个操作数a当作this指针传给这个重载函数,把右操作数b当作实参传给这个重载函数的形参A& s
。在重载函数A operator+(A& s)
的函数体内将A类对象a中的m_i和B类对象b中的m_i相加的值返回给A类的构造函数的形参,然后通过初始化列表给A类的数据成员m_i进行赋值为A类中对象m_i的值和B类对象m_i的值相加的结果。然后用这个相加后的对象去调用print函数,输出相加的结果。
因为运算符重载的实质为函数重载,所以可以把a+b;
写为a.operator+(b);
这两种写法完全相同。
减法运算符“-”的重载与加法运算符“+”的重载同理。
【例4】默认赋值运算符重载函数
class A
{
public:
A(int i = 0) :m_i(i) {}
void print()
{
cout << m_i << endl;
}
private:
int m_i;
};
int main()
{
A a(5);
A b(6);
(a = b).print();
return 0;
}
运行结果:
结果分析:
我们从程序中可以看出当前运算符“=”并没有重载,但是结果却没错,说明在类中给了一个默认的赋值运算符重载函数。默认的赋值运算符重载函数就是把当前的右边对象的数据成员的值依次赋值给左边对象的数据成员。这里的默认赋值重载函数是浅赋值。如果把这个默认赋值运算符重载函数写出来,就是这样:
A& operator=(const A& s)
{
if(this==&s)//判断自赋值
return *this;
m_i=s.m_i;
return *this;
}
所以 (a = b).print();
相当于是a.operator=(b)
,把a当作this指针传给这个重载函数,把b当作实参传给这个重载函数的形参。上面这个默认的赋值运算符重载函数的返回类型为引用类型,是因为如果使用值类型返回就会产生拷贝构造函数,比较麻烦,而这里由当前运算符组成的表达式可以放在=的左边,它具有相应的内存空间,所以使用引用作为默认的赋值运算符重载函数的返回类型。所以,最后的 return *this;
返回的就是当前对象a的数据成员m_i的值。
【例5】单目运算符“++”重载为成员函数。
class A
{
public:
A(int i = 0) :m_i(i) {}
void print()
{
cout << m_i << endl;
}
A operator++(int) //为了实现++的重载,语法规定,在后++的时候,参数里面写int,没有实际的意义,不用传参
{
cout << "调用函数operator++" << endl;
return m_i++;
}
A& operator++()
{
cout << "调用函数++operator" << endl;
m_i = m_i + 1;
return *this;
}
private:
int m_i;
};
void main()
{
A a(6);
A b(3);
A c(20);
//后置++
cout << "(a++)表达式的值:"<<endl;
(a++).print();//相当于a.++() //a++表达式的值是a没加以前的值6 a的值是a加过1之后的值7,最终返回的是表达式的值
//(a++)这个表达式在执行过程中有两个结果
cout << "a的值:" << endl;
a.print();
//前置++
cout << "(++b)表达式的值:" << endl;
(++b).print(); //b.++() ++b表达式的值是b加过1之后的值,b的值也是b加过1之后的值
//因为++b表达式的值和b的值是相同的,所以没有必要给++b这个表达式开辟空间,直接从b的内存单元中将这个值取出来,所以返回类型可以不用值类型,而是使用引用的返回类型
cout << "b的值:" << endl;
b.print();
}
运行结果:
结果分析:
①语法规定,在后置++的时候,参数里面写int,没有实际的意义,不用传参,它的目的是为了与前置++构成函数重载。
②后置++:(a++)表达式的值是a没加以前的值,a的值变为了a+1以后的值,所以后置++表达式有两个不同的值,但是最终返回的是表达式的值,因此,对于将后置++运算符重载为成员函数时的返回类型为值类型。
③前置++ :(++b)表达式的值和b的值一样,都是b+1以后的值,所以前置++表达式的值只有一个,因为++b表达式的值和b的值是相同的,所以没有必要给++b这个表达式开辟空间,直接从b的内存单元中将这个值取出来,所以返回类型可以不用值类型,而是使用引用的返回类型。