运算符重载规则
重载运算符的限制
可以重载的运算符
+ | - | * | / | % | ^ | & | | | ~ |
! | = | < | > | += | -= | *= | /= | %= |
^= | &= | |= | << | >> | >>= | <<= | == | != |
<= | >= | && | || | ++ | -- | ->* | ' | -> |
[] | () | new | delete | new[] | delete[] |
不能重载的算符
. | :: | .* | ?: | sizeof |
重载运算符函数可以对运算符作出新的解释,但原有基本语义不变
- 不改变运算符的优先级
- 不改变运算符的结合性
- 不改变运算符所需要的操作数
- 不能创建新的运算符
运算符重载的语法形式
运算符函数是一种特殊的成员函数或友员函数
成员函数的语法形式为1
2
3
4返回值类型 类名 :: operator op( 参数表 )
{
// 相对于该类定义的操作
}
一个运算符被重载后,原有意义没有失去,只是定义了相对一特定类的一个新运算符
用成员或友员函数重载运算符
运算符函数可以重载为成员函数或友员函数
关键区别在于成员函数具有 this 指针,友员函数没有this指针
不管是成员函数还是友员函数重载,运算符的使用方法相同
但传递参数的方式不同,实现代码不同,应用场合也不同
一元运算符
Object op或op Object
- 重载为成员函数,解释为
1
Object.operator op()
操作数由对象Object通过this指针隐含传递
- 重载为友员函数,解释为
1
operator op (Object)
操作数由参数表的参数Object提供
二元运算符
ObjectL op ObjectR
- 重载为成员函数,解释为:
1
ObjectL.operator op( ObjectR )
左操作数由ObjectL通过this指针传递,右操作数由参数ObjectR传递
- 重载为友员函数,解释为:
1
operator op( ObjectL, ObjectR )
左右操作数都由参数传递
用成员函数重载
当一元运算符的操作数,或者二元运算符的左操作数是类的对象时,定义重载算符函数为成员函数
用友员函数重载
友员函数重载运算符常用于运算符的左右操作数类型不同的情况
在第一个参数需要隐式转换的情形下,使用友员函数重载运算符是正确的选择
友员函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换
C++中不能用友员函数重载的运算符有 =
, ()
, []
, ->
友元函数重载存在的问题
用友员函数重载像“++”这样的运算符时,有时会碰到问题1
2
3
4
5TriCoor TriCoor::operator++()
{
x++; y++; z++;
return *this;
}//ok,修改了this指针所指对象
用成员函数重载一元运算符时,所需要的唯一变元通过 this 指针传递,对 this 所指对象数据的任何改变都会影响到激活运算符函数的对象
- 若定义友员函数 friend operator++()版本:
1
2
3
4
5
6
7TriCoor operator++(TriCoor opl)
{
opl.x++;
opl.y++;
opl.z++;
return opl;
}
函数使用传值参数,对 opl 的所有修改都无法传到函数体外,不会影响被调用的对象
- 用指向激活对象的指针定义友员函数:
1
2
3
4
5
6
7TriCoor operator ++ (TriCoor * opl)
{
opl->x++;
opl->y++;
opl->z++;
return *opl;
}
C++不知道如何激活该函数,下述代码无法编译:1
2TriCoor ob(1, 2, 3);
&ob++;//error
- 使用引用参数:
1
2
3
4
5
6
7TriCoor operator ++ (TriCoor & opl)
{
opl.x++;
opl.y++;
opl.z++;
return opl;
}
下述代码是正确的:1
2TriCoor ob (1, 2, 3);
ob++;//ok,传名
如果一个运算符的操作要修改类的对象的状态,要重载为友员函数时,应该使用引用参数
若一运算符的操作需要修改类对象状态时,应该用成员函数重载;
需要左值操作数的运算符(如 ++,–),若重载为友员函数时要用引用参数
C++不能用友员重载的运算符:=
()
[]
->
如果运算符的操作数(尤其是第一个操作数)希望有隐式转换,则必须用友员函数重载
几个典型运算符重载
数学类中常用的几个运算符重载的特点和应用
重载++与–
设 A Aobject;
运算符 ++ 和 – 有两种方式:
- 前置方式:++Aobject与–Aobject
一元成员函数重载A::A operator++ ();
,解释为:Aobject.operator ++();
友员函数重载friend A operator++ (A &);
,解释为:operator ++(Aobject);
- 后置方式:Aobject++与Aobject–
二元成员函数重载A::A operator++ (int);
,解释为:Aobject.operator ++(0);
友员函数重载:friend A operator++ (A &, int);
,解释为:operator++(Aobject, 0);
前置++重载1
2
3
4
5
6Complex& operator++()
{
this->a++;
this->b++;
return *this;
}
后置++重载1
2
3
4
5
6
7Complex operator++(int)
{
Complex tmp = *this;
this->a++;
this->b++;
return tmp;
}
重载赋值运算符
赋值运算符重载用于对象数据的复制
operator= 必须重载为成员函数
重载函数原型为:类型 & 类名::operator= (const 类名 &);
重载运算符[]和()
运算符 []
和 ()
是二元运算符[]
和 ()
只能用成员函数重载,不能用友元函数重载
重载下标运算符
[]
[]
运算符用于访问数据对象的元素
重载格式:类型 类::operator[] (类型);
e.g.
设x
是类X
的一个对象,则表达式x[y]
可被解释为x.operator[](y)
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
class vector
{
public:
vector(int n)
{
v = new int[n];
size = n;
}
~vector()
{
delete[] v;
size = 0;
}
int & operator[](int i)
{
return v[i];
}
private :
int *v;
int size;
};
void main()
{
vector a(5);
a[2] = 12;
cout << a[2] << endl;
}重载函数调用符
()
()
运算符用于函数调用
重载格式:类型 类::operator()(表达式表);
设x
是类X
的一个对象,则表达式x(arg1, arg2, … )
可被解释为x.operator()(arg1, arg2, … )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class F
{
public:
double operator()(double x , double y);
};
double F::operator()(double x,double y)
{
return x * x + y * y;
}
void main()
{
F f;
cout << f(5.2, 2.5) << endl;
}
重载流插入和流提取运算符
istream
和 ostream
是 C++ 的预定义流类cin
是 istream
的对象,cout
是 ostream
的对象
运算符 <<
由 ostream
重载为插入操作,用于输出基本类型数据
运算符 >>
由 istream
重载为提取操作,用于输入基本类型数据
用友员函数重载 <<
和 >>
,输出和输入用户自定义的数据类型1
2
3
4
5
6
7friend ostream& operator<<(ostream &out, Complex &c1);
···
ostream& operator<<(ostream &out, Complex &c)
{
out<<c.a<<" + "<<c.b<<"i"<<endl;
return out;
}
重载运算符[],=,==,!=项目演示
Array.h
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
class Array
{
private:
int mLength;
int* mSpace;
public:
Array(int length);
Array(const Array& obj);
int length();
void setData(int index, int value);
int getData(int index);
~Array();
public:
int& Array::operator[](int i);
Array& Array::operator=(Array &a1);
bool operator==(Array &a2);
bool operator!=(Array &a2);
};
Array.cpp
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
using namespace std;
Array::Array(int length)
{
if( length < 0 )
{
length = 0;
}
mLength = length;
mSpace = new int[mLength];
}
Array::Array(const Array& obj)
{
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
}
int Array::length()
{
return mLength;
}
void Array::setData(int index, int value)
{
mSpace[index] = value;
}
int Array::getData(int index)
{
return mSpace[index];
}
Array::~Array()
{
mLength = -1;
delete[] mSpace;
}
//以下是运算符重载函数
//[]重载
int& Array::operator[](int i)
{
return mSpace[i];
}
//=重载
Array& Array::operator=(Array &a)
{
int i = 0;
if (this->mSpace != NULL)
{
delete[] mSpace;
this->mLength = 0;
}
this->mLength = a.mLength;
this->mSpace = new int[a.mLength];
for (i=0; i<this->mLength; i++)
{
mSpace[i] = a[i];
}
return *this;
}
//==重载
bool Array::operator==(Array &a2)
{
if (this->mLength != a2.mLength)
{
return false;
}
for (int i=0; i<this->mLength; i++)
{
if (this->mSpace[i] != a2[i])
{
return false;
}
}
return true;
}
//!=重载
bool Array::operator!=(Array &a2)
{
return !(*this == a2);
}
ArrayTeat.cpp
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
using namespace std;
int main()
{
Array a1(10);
for(int i=0; i<a1.length(); i++)
{
//a1.setData(i, i);
a1[i] = i;
}
for(int i=0; i<a1.length(); i++)
{
//printf("array %d: %d\n", i, a1.getData(i));
printf("array %d: %d\n", i, a1[i]);
}
Array a2 = a1;
for(int i=0; i<a2.length(); i++)
{
//printf("array %d: %d\n", i, a2.getData(i));
printf("array %d: %d\n", i, a2[i]);
}
Array a3(20);
a2 = a3 = a1;
if (a1 == a2)
{
printf("相等\n");
}
else
{
printf("不相等\n");
}
if (a1 != a2)
{
printf("不相等\n");
}
else
{
printf("相等\n");
}
system("pause");
return 0;
}
类类型转换
数据类型转换在程序编译时或在程序运行实现
基本类型 ←→ 基本类型
基本类型 ←→ 类类型
类类型 ←→ 类类型
类对象的类型转换可由两种方式说明:
构造函数
转换函数
称为用户定义的类型转换或类类型转换,有隐式调用和显式调用方式
构造函数进行类类型转换
当类 ClassX
具有以下形式的构造函数:ClassX::ClassX(arg, arg1 = E1, ..., argn = En);
说明了一种从参数 arg 的类型到该类类型的转换
e.g.1
2
3
4
5
6
7
8
9
10
11
12
13class X
{
public:
X(int);
X(const char *, int = 0);
};
void f(X arg);
···
X a = X(1);// a = 1
X b = "Jessie";// b = X ( "Jessie" , 0 )
a = 2;// a = X ( 2 )
f(3);// f ( X ( 3 ) )
f(10, 20);// error
类型转换函数
带参数的构造函数不能把一个类类型转换成基本类型
类类型转换函数是一种特殊的成员函数,提供类对象之间显式类型转换的机制
语法形式:1
2
3
4
5X::operator T()
{
···
return T 类型的对象
}
功能:将类型 X
的对象转换为类型 T
的对象
T
可以是预定义类型,也可以是用户定义类型- 函数没有参数,没有返回类型,但必须有一条 return 语句,返回
T
类型的对象 - 该函数只能为成员函数,不能为友员
1
2
3
4
5
6
7
8
9
10
11
12
13class X
{
···
public:
operator int();
···
};
void f(X a)
{
int i = int(a);
i = (int)a;
i = a;
}
类型转换函数有两种使用方式:
- 隐式使用
i = a;
- 显式使用
i = a.operator int();//int(a) (int)a
使用不同函数作类型转换函数:1
2int i = a;//用类型转换函数进行转换
X i = a;//用构造函数进行转换
运算符重载运用
全局函数,类成员函数实现运算符重载
- 承认操作符重载是一个函数,写出函数名称
- 根据操作数写出函数参数
- 根据业务,完善函数返回值,实现函数业务
全局函数:写出函数实现,在类中添加友元函数声明,一般只有拿不到源代码的类参与才会用全局函数实现
操作符重载的方法
- 全局函数友元函数法(技术推演)
private声明使得类的成员不能被外界访问
但是通过friend关键字可以例外的开放权限 通过成员函数进行操作符的重载
用成员函数重载的操作符,左操作数,通过this指针隐含传送
比全局函数友元函数法少一个参数,即左操作数;不需要使用friend关键字1
2Complex c3 = c1 + c2;
Complex c3 = c1.operator+(c2);什么时候用全局函数重载操作符PK什么时候用成员函数重载操作符
- 当无法修改左操作数的类时,使用全局函数进行重载
- =, [], ()和->操作符只能通过成员函数进行重载
运算符重载总结
- 运算符重载可以像基本数据类型一样,用简洁明确的运算符操作自定义的类对象。
- 重载运算符函数可以对运算符作出新的解释,但原有基本语义不变。
- 运算符函数既可以重载为成员函数,也可以重载为友员函数或普通函数。
- 当一元运算符的操作数,或者二元运算符的左操作数是类的一个对象时,以成员函数重载;当一个运算符的操作需要修改类对象- 状态时,应该以成员函数重载。如果以成友员函数重载,则使用引用参数修改对象。
- 当运算符的操作数(尤其是第一个操作数)希望有隐式转换,则重载算符时必须用友员函数。
- 构造函数和类型转换函数可以实现基本类型与类类型,以及类类型之间的类型转换