概念
1.C++提供的运算符,通常只支持对于基本数据类型和标准库中提供的类进行操作,对于自定义类型如果想通过操作符实现对应的操作,需要自定义重载的操作符并实现具体的功能。
2.重载操作符:也称为重载运算符,本质上是一个函数,而且是一种特殊的重载操作符函数,并告诉 C++编译器,当遇到该运算符时就调用此函数来行使运算符功能,其本质是通过函数扩展了操作符的功能。重载操作符函数需要使用关键字operator作为函数名的一部分,重载操作符一般要有返回值,方便继续和其他操作符去操作。
3.可分两类:类内重载、类外重载。
类内重载操作符
在类内重载,作为类成员函数,需要用对象调用,使用场景需要与函数的参数一致(包括类型和顺序)。注意在类内重载的操作符函数有隐藏的this指针作为第一个参数。在使用是要注意重载操作符的参数类型和顺序,可以直接使用操作符,也可以显示通过对象调用重载的操作符。
#include<iostream>
using namespace std;
class CTest {
public:
int m_a;
CTest() :m_a(1) {
this->m_a = m_a;
}
int add(int a) {
return this->m_a + a;
}
int operator+(/*CTest * const this*/int a) {
return this->m_a + a;
}
int operator+=(int a) {
return m_a += a;
}
};
int main() {
CTest tst;
int a = tst + 20;
int b = tst.operator+(20);
int c = tst += 20;
cout << a << " " << b <<" "<<c << " " << tst.add(20) << endl;
//int d = 20 + tst; //error
return 0;
}
输出结果:
对于单目运算符++,有左++ 和右++ 两种,为了区分右++,我们需要额外指定一个int类型的参数,这个参数只是用来区分,并无实际意义。
#include<iostream>
using namespace std;
class CTest {
public:
int m_a=10;
//左++
int operator++() {
return ++this->m_a;
}
//右++
int operator++(int a) {
return this->m_a++;
}
};
int main() {
CTest tst;
cout << tst.operator++()<<endl;
return 0;
}
类外重载操作符
在类外重载,至少需要包含一个自定义类型,在类内的重载操作符函数有默认的this指针且为自定义类型,所以在定义函数时忽略了第一个参数,但类外重载没有隐藏的参数,一般比类内要多一个参数。但要注意是否与类内重载的函数有冲突。
//类外重载
int operator+(CTest &tst,int a) {
return tst.m_a + a;
}
类内、类外产生歧义,调用不明确:
class CTest {
public:
int m_a;
CTest() {
m_a = 10;
}
//类内重载
int operator+(int a) {
return this->m_a+a;
}
};
//类外重载
int operator+(CTest &tst,int a) {
return tst.m_a + a;
}
int main() {
CTest tst;
//tst + 10; //error
tst.operator+(10);//类内
::operator+(tst, 10);//类外
return 0;
}
自定义重载输入、输出操作符,一般在类外重载:
class CTest {
public:
int m_a;
};
//输入
istream& operator>>(istream& is,CTest& tst) {
is >> tst.m_a;
return is;
}
//输出
ostream& operator<<(ostream& os, CTest& tst) {
os << tst.m_a;
return os;
}
对于同一个操作符来说,写在不同的位置代表不同的含义,那么重载这个操作符需要注意参数的量、顺序不同代表不同的含义。
//类内重载
int operator*();//间接引用
int operator*(int);//乘法
几个注意事项:
1.不能重载的运算符 :长度运算符sizeof、条件运算符: ?、成员选择符.、作用域运算符::等。
2.还有一些操作符只能在类内重载,赋值= ,下标[ ],调用(),和成员指向-> 操作符,它们必须被定义为类成员操作符。
3.重载操作符不能改变操作符的用法,原来有几个操作数、操作数在左边还是在右边,这些都不会改变。
4.运算符重载函数不能有默认的参数,否则就改变了运算符操作数的个数,这显然是错误的。
5.重载操作符不能改变运算符的优先级和结合性。
6.不能创建新的运算符。
对象类型转换
图中可以看到,我们反过来写会报错,类型不匹配。
此时可以重载某个类型,这样定义该类对象就可以像这个类型一样去使用。
引入转换函数(注:转换函数必须是非静态成员函数):
函数格式:
注:函数无参数、无返回值,但函数体中应该有return,且return的变量类型要和重载的类型一致。
下面两种都是错误的写法:
如果同时存在重载操作符和重载类型,那么优先匹配重载的操作符。
当然也可以显示的调用类型转换函数
完整代码:
#include<iostream>
using namespace std;
class CTest {
public:
int m_a = 1;
int operator+(int a)
{
return m_a + a;
}
operator int()
{
int a = 0;
return a;
}
};
int main()
{
CTest tst;
int a = tst + 10; //operator+
a = 10 + tst; //operator int
a = tst; //operator int
}
实战训练:封装迭代器