运算符重载:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
1operator符号(){};
在运算符重载的形式为 operator()
的情况下,其中的 ()
表示函数调用运算符,用于对对象进行类似函数调用的操作,可以将对象当作函数使用。
在这种情况下,1
的位置可以是任意的,它不会影响运算符重载的效果。1
可以是任意参数、返回类型或函数修饰符。这个位置通常用于指定参数类型和传递给运算符重载函数的实际参数。
而 ()
中一般不需要写任何内容,因为 ()
是表示函数调用的语法,而不是函数的定义。运算符重载函数体应该写在 {}
中,用于定义运算符重载的具体实现。
下面是一个函数调用运算符的例子:
class MyClass {
public:
int operator()(int x, int y) {
return x + y;
}
};
int main() {
MyClass obj;
int result = obj(3, 7); // 调用运算符重载的效果类似于 obj.operator()(3, 7)
// result = 10
return 0;
}
cpp复制代码
在这个例子中,运算符重载函数 operator()
接受两个 int
类型的参数,并返回它们的和。在 main()
函数中,我们创建了一个 MyClass
对象 obj
并使用括号运算符将其当作函数进行调用。最终得到的 result
值为 10
。
17.1加号运算符重载
作用:实现两个自定义数据类型相加的运算
17.1.1成员函数运算符重载+号
#include<iostream>
using namespace std;
//加号运算符重载
class Person
{
public:
//成员函数运算符重载+号
Person operator+(Person& p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;//让自身的A属性和传进来的A属性相加赋值给新的对象
temp.m_B = this->m_B + p.m_B;
return temp;
}
int m_A;
int m_B;
};
void test01()
{
Person p1;
Person p2;
p1.m_A = 10;
p1.m_B = 10;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;//会报错(没有与这些操作数匹配的“+”运算符),在class Person中加Person operator+(Person& p)等就不会报错了
//成员函数重载本质调用 Person p3=p1.operator+(p2);
cout << "p3.m_A=" << p3.m_A << endl;
cout << "p3.m_B=" << p3.m_B << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
17.1.2全局函数运算符重载+号
#include<iostream>
using namespace std;
//加号运算符重载
class Person
{
public:
int m_A;
int m_B;
};
//全局函数重载+号
Person operator+(Person& p1, Person& p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
void test01()
{
Person p1;
Person p2;
p1.m_A = 10;
p1.m_B = 10;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;//会报错(没有与这些操作数匹配的“+”运算符),在全局函数中加Person operator+(Person& p1, Person& p2)等就不会报错了
//全局函数本质调用Person p3 = operator+(p1, p2);
cout << "p3.m_A=" << p3.m_A << endl;
cout << "p3.m_B=" << p3.m_B << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
17.1.3运算符重载也可以发生函数重载
#include<iostream>
using namespace std;
//加号运算符重载
class Person
{
public:
int m_A;
int m_B;
};
//全局函数重载+号
Person operator+(Person& p1, Person& p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
//函数重载的版本
Person operator+(Person& p1, int num)
{
Person temp;
temp.m_A = p1.m_A + num;
temp.m_B = p1.m_B + num;
return temp;
}
void test01()
{
Person p1;
Person p2;
p1.m_A = 10;
p1.m_B = 10;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;//会报错(没有与这些操作数匹配的“+”运算符),在全局函数中加Person operator+(Person& p1, Person& p2)等就不会报错了
//全局函数本质调用Person p3 = operator+(p1, p2);
//运算符重载,也可以发生函数重载
Person p4 = p1 + 100;//Person+int
cout << "p3.m_A=" << p3.m_A << endl;
cout << "p3.m_B=" << p3.m_B << endl;
cout << "p4.m_A=" << p4.m_A << endl;
cout << "p4.m_B=" << p4.m_B << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
总结:对于内置的数据类型的表达式的运算符是不可能改变的(+还是+,+不能变成-)
不要滥用运算符重载(在外面写两个数相加,但是函数重载内写两个数相减等,这就是滥用,不容易让其他人看懂代码)
17.2左移运算符重载
作用:可以输出自定义数据类型
#include<iostream>
using namespace std;
class Person
{
//friend operator<<(ostream& cout, Person& p)//添加友元
public://如果改成private私有那么下面函数都会访问不到m_A,m_B,解决办法是添加友元
Person(int a,int b)
{
m_A=a;
m_B=b;
}
//尝试利用成员函数左移重载运算符
//p.operator<<(cout)简化版本p<<cout
//不会利用成员函数重载<<运算符,因为无法实现cout在左侧
//void operator<<(cout)
//{
//}
//所以,只能利用全局函数重载左移运算符
//private:
int m_A;
int m_B;
};
//void operator<<(ostream& cout, Person& p)
ostream &operator<<(ostream& cout, Person &p)//本质operator<<(cout,p)简化cout<<p,因为一局只有一个cout所以加&
{ //cout的数据类型为ostream(输出流)
cout << "m_A=" << p.m_A << " m_B=" << p.m_B;
return cout;//因为返回cout,而cout的数据类型为ostream所以void operator<<(ostream& cout, Person& p)里void operator改成ostream &operator
}//如果把ostream &operator<<(ostream& cout, Person &p)内的cout都换成out也可以运行,因为这是引用
void test01()
{
Person p(10,20);
cout << p <<endl;//正常会报错,因为链式思维,返回的得是cout才能继续往下走,p向后传输都是void了,所以上面得改成cout的数据类型
}
int main()
{
test01();
system("pause");
return 0;
}
总结:重载左移运算符配合友元可以实现输出自定义数据类型
ostream是cout的类型
17.3递增运算符重载
作用:通过重载递增运算符,实现自己的整型数据
#include<iostream>
using namespace std;
//重载递增运算符
//自定义整型
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger()
{
m_Num = 0;
}
//重载++运算符(分两种,重载前置++运算符,重载后置++运算符)
//重载前置++运算符 返回引用为了一直对一个数据进行递增
MyInteger& operator++()//想要重载使自制的数据类型能够++运算
{
m_Num++;//在void operator++()下直接写的话会使cout不知道怎么输出
return *this;//自身解引用操作
//先进行++操作,再将自身返回
}
//重载后置++运算符
//void operator++(int)中的int代表占位参数,可以用于区分前置和后置递增
MyInteger operator++( int )//返回值不是作为函数重载的区分条件,void operator++()加个int区分MyInteger& operator++()
{ //函数重载的满足条件:同一个作用域下,函数名一样,参数的类型、个数、排序不一样
//先记录当时结果
MyInteger temp = *this;
//后递增
m_Num++;
//最后将记录结果做返回
return temp;
}
//前置递增返回引用,后置递增返回值
private:
int m_Num;
};
//重载<<运算符
ostream& operator << (ostream & cout, MyInteger myint)
{
cout << myint.m_Num;
return cout;
}
void test01()
{
MyInteger myint;
cout << myint << endl;
cout << ++myint << endl;//直接写会报错,因为编译器不知道怎么处理人工写的数据类型怎么运算
//解决办法:重载运算符
cout << ++(++myint) << endl;//第一次是让myint进行加1,第二次是让++mying加1,所以需要解引用操作return *this;
cout << myint << endl;//做了解引用操作输出值为3,没做解引用输出值为2
}
void test02()
{
MyInteger myint;
cout << myint++ << endl;
cout << myint << endl;
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
17.4赋值运算符重载
c++编译器至少给一个类添加4个函数
(1)默认构造函数(无参,函数体为空)
(2)默认析构函数(无参,函数体为空)
(3)默认拷贝构造函数,对属性进行值拷贝
(4)赋值运算符operator=。对属性进行值拷贝
如果类又属性指向堆区,做赋值操作时也会出现深浅拷贝问题
#include<iostream>
using namespace std;
//赋值运算符重载
class Person
{
public:
Person(int age)
{
m_Age=new int(age);//new int(age)把数据开辟到堆区,m_Age=让指针维护堆区的数据
}
~Person()//因为p2和p1连接的是同一块内存,重复释放同一块内存,所以报错
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
//重载 赋值运算符
Person& operator=(Person& p)
{
//编译器是提供浅拷贝
//m_Age = p.m_Age;
//应该先判断是否有属性再堆区,如果有先释放干净,然后再深拷贝
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
//深拷贝
m_Age = new int(*p.m_Age);
//返回对象本身
return *this;
}
int *m_Age;
};
void test01()
{
Person p1(18);
Person p2(20);
Person p3(30);
p2 = p1;//赋值操作,浅拷贝
p3 = p2 = p1;//需要让p1返回p2自身而不是void
cout << "p1的年龄为:" << *p1.m_Age<< endl;
cout << "p2的年龄为:" << *p2.m_Age << endl;
cout << "p3的年龄为:" << *p3.m_Age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
17.5关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
#include<iostream>
using namespace std;
//重载关系运算符
class Person
{
public:
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
//重载==号
bool operator==(Person& p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
bool operator!=(Person& p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return false;
}
return true;
}
string m_Name;
int m_Age;
};
void test01()
{
Person p1("Tom", 18);
Person p2("Tom", 18);
if (p1 == p2)
{
cout <<"p1和p2是相等的" << endl;
}
else
{
cout << "p1和p2是不相等的" << endl;
}
if (p1 != p2)
{
cout << "p1和p2是不相等的" << endl;
}
else
{
cout << "p1和p2是相等的" << endl;
}
}
int main()
{
test01();
system("pause");
return 0;
}
17.6函数调用运算符重载
函数调用运算符()也可以重载
由于重载后使用的方式非常像函数的调用,因此称为仿函数
仿函数没有固定的写法,非常灵活
#include<iostream>
using namespace std;
//函数调用运算符重载
//打印输出类
class MyPrint
{
public:
//重载函数调用运算符
void operator()(string test)
{
cout << test << endl;
}
};
void MyPrint02(string test)//函数调用
{
cout << test << endl;
}
void test01()
{
MyPrint myPrint;
myPrint("hello world");//传给了void operator()(string test)里的test
//因为使用起来非常类似于函数调用,因此称为仿函数
MyPrint02("hello world");//函数调用
}
//仿函数非常灵活,没有固定的写法
//加法类
class MyAdd
{
public:
int operator()(int num1, int num2)
{
return num1 + num2;
}
};
void test02()
{
MyAdd myadd;
int ret = myadd(100, 100);
cout << "ret=" << ret << endl;
//匿名对象,特点:当前执行完了直接被释放
cout << MyAdd()(100, 100) << endl;
}
int main()
{
test01();
test02();
system("pause");
return 0;
}