第13课 - 操作符重载 - 下
一.类的成员函数作为操作符重载函数
Source Example 1:
#include <iostream>
using namespace std;
class Complex {
int a;
int b;
public:
Complex(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
/* 成员函数有一个隐藏的参数this指针,注意第一个参数是隐藏的this指针 */
Complex operator+ (const Complex& c2 )
{
Complex ret;
/* ret.a = this->a + c2.a; */
/* ret.b = this->b + c2.b; */
ret.a = a + c2.a;
ret.b = b + c2.b;
return ret;
}
int GetA()
{
return a;
}
int GetB()
{
return b;
}
friend ostream& operator<< (ostream& out, const Complex& c);
//friend Complex operator+ (const Complex& c1, const Complex& c2 );
};
ostream& operator<< (ostream& out, const Complex& c)
{
out<<c.a<<" + "<<c.b<<"i";
return out;
}
int main()
{
Complex c1(1,2);
Complex c2(1,3);
/* c1+c2等价于c1.operator+(c2) */
Complex c3 = c1 + c2;
cout<<c1<<endl;
cout<<c2<<endl;
cout<<c3<<endl;
return 0;
}
注意:操作符重载的成员函数左操作符就是this指针,因此会比全局操作符重载函数少一个参数。
1.2 用成员函数重载操作符
比全局操作符重载函数少一个参数,即左操作数
不需要使用friend关键字
二.什么时候使用全局操作符重载函数,什么时候用成员操作符重载函数?
2.1 当无法修改左操作数的类时,使用全局函数进行重载
例如左移操作符的重载,左操作符为ostream的类,是C++标准库定义的类,不可修改,因此要用全局函数进行重载。
2.2 =,[],(), ->操作符只能通过成员函数进行重载
Source Example 2.2.1:(数组类的改进,[]操作符的重载)
/* 以前的数组类声明 Array a(10);不像数组,因此我们对[]操作符进行重载 */
/* 对[]操作符的重载只能使用成员函数 */
/* array.h */
#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array{
private:
int iLength;
int *pSpace;
public:
Array(int length);
Array(const Array& a);
int getLength();
~Array();
int& operator[] (int i);
};
#endif /* _ARRAY_H_ */
/* array.c */
#include "array.h"
Array :: Array(int length)
{
iLength = length;
if (length > 0)
{
pSpace = new int[length];
}
}
int Array :: getLength()
{
return iLength;
}
Array :: Array(const Array& obj)
{
iLength = obj.iLength;
pSpace = new int[iLength];
for (int i = 0; i < iLength; i++)
{
pSpace[i] = obj.pSpace[i];
}
}
/* 此处返回的是必须一个引用
* 当调用a[i] = i;时,等价于 a.operator[](i) = i;
* 如果要把一个函数的调用语句作为左值来使用,那么这个函数必须要返回一个引用
*
* 由于函数返回值会产生一个临时变量作为返回值的副本,只是一个值。
* 返回引用会返回变量,因此可以作为左值使用
*/
int& Array :: operator[] (int i)
{
return pSpace[i];
}
Array :: ~Array()
{
delete[] pSpace;
}
/* main.cpp */
#include <iostream>
#include "array.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
Array a(10);
for (int i = 0; i < 10; i++)
{
a[i] = i;
}
for (int j = 0; j < a.getLength(); j++)
{
printf ("%d\n", a[j]);
}
Array a2 = a;
for (int j = 0; j < a2.getLength(); j++)
{
printf ("%d\n", a2[j]);
}
return 0;
}
注意: 当调用函数作为左值使用时,返回值必须是一个引用,不能是值!
由于函数返回值会产生一个临时变量作为返回值的副本,只是一个值。
返回引用会返回变量,因此可以作为左值使用
Source Example 2.2.2:(数组类的改进,=操作符的重载)
/* array.h */
#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array{
private:
int iLength;
int *pSpace;
public:
Array(int length);
Array(const Array& a);
int getLength();
~Array();
int& operator[] (int i);
Array& operator= (const Array& obj);
};
#endif /* _ARRAY_H_ */
/* array.c */
#include "array.h"
#include <iostream>
Array :: Array(int length)
{
iLength = length;
if (length > 0)
{
pSpace = new int[length];
}
printf ("pSpace = %08x\n", pSpace);
}
int Array :: getLength()
{
return iLength;
}
Array :: Array(const Array& obj)
{
iLength = obj.iLength;
pSpace = new int[iLength];
printf ("pSpace = %08x\n", pSpace);
for (int i = 0; i < iLength; i++)
{
pSpace[i] = obj.pSpace[i];
}
}
int& Array :: operator[] (int i)
{
return pSpace[i];
}
/*
* 对=操作符的重载函数,与拷贝构造函数的重载相同,只是需要删除申请pSpace
* 注意需要返回Array&,因为为了实现连续赋值
* a3 = a2 = a1;
* 具体原因与cout的重载原因相同
* 函数调用作为左值必须要返回引用
*/
Array& Array :: operator= (const Array& obj)
{
delete[] pSpace;
iLength = obj.iLength;
pSpace = new int[iLength];
for (int i = 0; i < iLength; i++)
{
pSpace[i] = obj.pSpace[i];
}
/* 返回当前对象 */
return *this;
}
Array :: ~Array()
{
delete[] pSpace;
}
/* main.cpp */
#include <iostream>
#include "array.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
Array a(10);
/* 定义一个对象a2,初始化没有调用拷贝构造函数 */
Array a2(1);
Array a3(2);
for (int i = 0; i < 10; i++)
{
a[i] = i;
}
for (int j = 0; j < a.getLength(); j++)
{
printf ("%d\n", a[j]);
}
/*
* 不会调用拷贝构造函数,因为已经初始化了,只是单纯的=赋值
* 但是=赋值只是进行单纯的复制,导致两个的pSpace只想同一块内存
* 因此,需要对=操作符进行重载
*/
a2 = a;
for (int j = 0; j < a2.getLength(); j++)
{
printf ("%d\n", a2[j]);
}
return 0;
}
Source Example 2.2.3:(数组类的改进,==操作符的重载)
#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array{
private:
int iLength;
int *pSpace;
public:
Array(int length);
Array(const Array& a);
int getLength();
~Array();
int& operator[] (int i);
Array& operator= (const Array& obj);
bool operator== (const Array& obj);
bool operator!= (const Array& obj);
};
#endif /* _ARRAY_H_ */
/* array.c */
#include "array.h"
#include <iostream>
Array :: Array(int length)
{
iLength = length;
if (length > 0)
{
pSpace = new int[length];
}
printf ("pSpace = %08x\n", pSpace);
}
int Array :: getLength()
{
return iLength;
}
Array :: Array(const Array& obj)
{
iLength = obj.iLength;
pSpace = new int[iLength];
printf ("pSpace = %08x\n", pSpace);
for (int i = 0; i < iLength; i++)
{
pSpace[i] = obj.pSpace[i];
}
}
int& Array :: operator[] (int i)
{
return pSpace[i];
}
Array& Array :: operator= (const Array& obj)
{
delete[] pSpace;
iLength = obj.iLength;
pSpace = new int[iLength];
for (int i = 0; i < iLength; i++)
{
pSpace[i] = obj.pSpace[i];
}
return *this;
}
/* 重载比较操作符 */
bool Array :: operator== (const Array& obj)
{
bool ret = true;
if (iLength == obj.iLength)
{
for (int i = 0; i < iLength; i++)
{
if (pSpace[i] != obj.pSpace[i])
{
ret = false;
break;
}
}
}
else
ret = false;
return ret;
}
bool Array :: operator!= (const Array& obj)
{
return !(*this == obj);
}
Array :: ~Array()
{
delete[] pSpace;
}
/* main.cpp */
#include <iostream>
#include "array.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
Array a(10);
Array a2(1);
Array a3(2);
for (int i = 0; i < 10; i++)
{
a[i] = i;
}
for (int j = 0; j < a.getLength(); j++)
{
printf ("%d\n", a[j]);
}
a3 = a2 = a;
if (a2 == a)
{
printf ("Hello World!\n");
}
for (int j = 0; j < a2.getLength(); j++)
{
printf ("%d\n", a2[j]);
}
return 0;
}
总结:
a.C++编译器会为每个类提供默认的赋值操作赋
b.默认的复制操作赋只是进行简单的值复制
c.类中存在指针成员变量时就需要重载赋值操作符
三.++操作符的重载
3.1 ++操作符只有一个操作数
3.2 ++操作符有前缀和有后缀之分
如何区分++操作符的前缀和后缀呢?
++操作符重载也是通过函数重载实现的
C++中通过一个占位参数来区分前置操作符和后置操作符
Source Example 3:
#include <iostream>
using namespace std;
class Complex {
int a;
int b;
public:
Complex(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
/* 成员函数有一个隐藏的参数this指针 */
Complex operator+ (const Complex& c2 )
{
Complex ret;
ret.a = a + c2.a;
ret.b = b + c2.b;
return ret;
}
/* 后置++ */
Complex operator++ (int);
/* 前置++ */
Complex& operator++ ();
int GetA()
{
return a;
}
int GetB()
{
return b;
}
friend ostream& operator<< (ostream& out, const Complex& c);
};
/* 不能返回引用,因为ret是局部变量 */
Complex Complex :: operator++ (int)
{
Complex ret = *this;
a++;
b++;
return ret;
}
/* 前置++操作符比后置++操作符效率高很多 */
Complex& Complex :: operator++ ()
{
a++;
b++;
return *this;
}
ostream& operator<< (ostream& out, const Complex& c)
{
out<<c.a<<" + "<<c.b<<"i";
return out;
}
int main()
{
Complex c1(1,2);
Complex c2(1,3);
c1 = c2;
c1++;
++c2;
printf ("c1.a = %d, c1.b = %d\n", c1.GetA(), c1.GetB());
printf ("c2.a = %d, c2.b = %d\n", c2.GetA(), c2.GetB());
return 0;
}
四.不要重载&&和||操作符
Source Example 4:
#include <iostream>
using namespace std;
class Test {
int i;
public:
Test (int i)
{
this->i = i;
}
Test operator+ (const Test& obj)
{
Test ret = 0;
/* 先打印了这一局 */
printf ("this->i + obj.i!\n");
ret.i = i + obj.i;
return ret;
}
bool operator&& (const Test& obj)
{
/* 其次打印这一句 */
printf ("this->i && obj.i!\n");
return (i && obj.i);
}
};
int main()
{
int a1 = 0;
int a2 = 1;
/* 当a1=0时,(a1+a2)就不会被执行 */
if (a1 && (a1 + a2))
{
cout<<"Hello World!"<<endl;
}
Test t1 = 0;
Test t2 = 1;
/* 等价于 t1.operator&&(t1.operator+(t2)) */
/* 因此会先执行t1+t2,与&&操作符的含义并不相同 */
if (t1 && (t1 + t2))
{
cout<<"Hello World!\n"<<endl;
}
return 0;
}
输出结果如下
总结:
&&和||是C++中非常特殊的操作符
&&和||内实现了短路规则
操作符重载是靠函数重载完成的
操作数作为函数参数传递
C++中的函数参数都会被求值,无法实现短路规则
五.小结
5.1 操作符重载可以直接使用类的成员函数实现
5.2 =,[],(),->操作符只能通过成员函数进行重载
5.3 ++操作符通过一个Int参数进行前置与后置的重载
5.4 C++中不要重载&&和||操作符