运算符重载定义
运算符重载与函数重载一样!
运算符的重载实质是函数的重载
一般格式:
函数类型 operator 运算符名称 (行参表列)
{
//对运算符重载的处理
}
运算符重载
成员函数前提:第一个操作数必须是本类对象。
举几个例子:
加法运算符重载
#include<iostream>
#include<string>
using namespace std;
//加号运算符重载
class Person
{
public:
//1.成员函数重载+
/*Person operator+(Person& p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}*/
int m_A;
int m_B;
};
//2.全局函数重载+
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;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
//成员函数重载本质调用
//Person p3 = p1.operator+(p2);
//Person p3 = p1 + p2;//可以简化成这种形式
//全局函数重载的本质调用
//Person p3 = operator+(p1,p2);
/*cout << p3.m_A << endl;
cout << p3.m_B << endl;*/
//运算符重载也可以发生函数重载
Person p3 = p1 + 10;
cout << p3.m_A << endl;
cout << p3.m_B << endl;
}
int main(void)
{
test01();
system("pause");
return 0;
}
左移运算符重载
#include<iostream>
using namespace std;
class Person
{
friend ostream& operator<<(ostream& cout, Person& p);
public:
Person(int a, int b)
{
m_A = a;
m_B = b;
}
//利用成员函数重载左移运算符p.operator<<(cout)简化版本p<<cout
//一般我们不会利用成员函数来重载<<运算符,以为无法实现cout在左边
/*void operator<<(ostream &cout,Person &p)
{
cout << p.m_A << endl;
cout << p.m_B << endl;
}*/
private:
int m_A;
int m_B;
};
//只能利用全局函数来重载左移运算符
ostream& operator<<(ostream &cout, Person &p) //这样写的本质就是operator<<(cout,p)简化版本就是cout<<p;
{
cout << p.m_A << endl;
cout << p.m_B << endl;
return cout;
}
void test()
{
Person p(10,10);
cout << p << "hello world" << endl;
}
int main(void)
{
test();
system("pause");
return 0;
}
递增运算符重载
#include<iostream>
using namespace std;
//重载递增运算符
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger()
{
m_Num = 0;
}
//重载++运算符——前置
//返回引用是为了一直对一个数据进行递增操作
MyInteger& operator++()
{
++m_Num;
return *this;
}
//重载++运算符——后置
MyInteger operator++(int)//这个int在这里作为占位参数,用来区分前置递增和后置递增
{
MyInteger temp = *this;
m_Num++;
return temp;
//后置递增要返回值,因为如果返回引用,这里相当于返回的是一个局部对象的引用。
//局部对象在当前函数执行完毕之后就被释放掉了,还要返回引用就是非法操作。
}
private:
int m_Num;
};
//全局函数重载左移运算符
ostream& operator<<(ostream& cout, MyInteger myint)
{
cout << myint.m_Num << endl;
return cout;
}
void test()
{
MyInteger myint;
cout << ++(++myint);
cout <<myint;
}
void test02()
{
MyInteger myint;
cout << myint++ << endl;
cout << myint << endl;
}
int main(void)
{
//test();
test02();
system("pause");
return 0;
}
在C++中,使用int参数来区分前置递增运算符(++x)和后置递增运算符(x++)并不是因为int参数本身有任何实际意义,而是利用了函数签名(包括返回类型和参数列表)的唯一性这一语言特性。具体原因如下:
函数重载:C++允许在同一作用域内定义多个同名函数,只要它们的参数列表(包括参数类型和数量)不同。这就是函数重载机制。当调用重载函数时,编译器会根据实参列表与形参列表的匹配情况来确定调用哪个版本的函数。
约定与识别:为了能够同时支持前置和后置递增运算符,而又不让它们的函数名冲突,C++采用了一种约定:对于后置递增运算符,额外提供一个无用的int参数。这个参数并不用于接收实际值,其存在的唯一目的是使得后置版本的函数签名与前置版本不同,从而满足函数重载的要求。由于这个约定是语言标准的一部分,编译器在解析代码时会根据上下文自动识别出++是前置还是后置。
语法解析:在表达式++x和x++中,编译器根据++相对于操作数的位置(在前还是在后)来决定调用哪个版本的operator++。当++位于操作数前面时,编译器查找不带额外参数的版本;当++位于操作数后面时,编译器查找带有int参数的版本。由于这两种形式的函数签名不同,编译器能够准确无误地选择对应的重载版本。
赋值运算符重载
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
m_Age = new int(age);
}
~Person()
{
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 test1()
{
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1;
cout << *(p1.m_Age) << endl;
cout << *(p2.m_Age) << endl;
cout << *(p3.m_Age) << endl;
}
int main(void)
{
test1();
system("pause");
return 0;
}
关系运算符重载
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
//重载==
bool operator==(Person &p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
else
{
return false;
}
}
bool operator!=(Person &p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return false;
}
else
{
return true;
}
}
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
string m_Name;
int m_Age;
};
void test()
{
Person p1("张三", 20);
Person p2("张三", 20);
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(void)
{
test();
system("pause");
return 0;
}
函数运算符重载
#include<iostream>
#include<string>
using namespace std;
//函数调用运算符重载
class MyPrint
{
public:
//重载函数调用运算符
void operator()(string text)
{
cout << text << endl;
}
};
class MyAdd
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
void test()
{
MyPrint myprint;
myprint("hello world");
MyAdd myadd;
cout << myadd(1, 2) << endl;
//匿名函数对象——特点:当前行被执行完立即释放
cout << MyAdd()(100,100) << endl;
}
int main(void)
{
test();
system("pause");
return 0;
}
运算符重载规则
不能改变运算符的优先级和结合性:你不能通过重载来改变运算符的优先级或结合性。
例如,+ 和 - 运算符始终是左结合的,你不能通过重载来改变这一点。
不能创建新的运算符:你只能重载 C++ 已有的运算符,不能创建新的运算符
不是所有运算符都可以被重载 有些运算符不能被重载
例如 .、.*、::、?:、sizeof 和类型转换运算符等不能被重载。
重载运算符必须至少有一个操作数是自定义类型
这防止了你改变内置类型的行为。
举个例子,你不能重载 + 运算符让它仅对两个 int 类型操作数有效,因为 + 对于 int 类型的行为已经由语言本身定义了。
但是,你可以重载 + 运算符让它对一个 int 和一个自定义类型,或者两个自定义类型有效。
class Complex {
public:
double real;
double imag;
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
// 重载 + 运算符以用于两个 Complex 对象
Complex operator+(const Complex& rhs) const
{
return Complex(real + rhs.real, imag + rhs.imag);
}
// 也可以重载 + 运算符以用于一个 int 和一个 Complex 对象
Complex operator+(int scalar) const
{
return Complex(real + scalar, imag);
}
};
int main()
{
Complex c1(1.0, 2.0);
Complex c2(3.0, 4.0);
Complex c3 = c1 + c2; // 使用重载的 + 运算符
Complex c4 = c1 + 5; // 使用重载的 + 运算符与 int
}
重载的运算符不能改变其语义:
例如,你不能将 + 运算符重载为减法运算。
重载的运算符可以是成员函数或非成员函数:
成员函数通常用于处理涉及单个对象的运算,而非成员函数通常用于处理涉及两个对象的运算。
重载的运算符可以是友元函数:
如果运算符需要访问类的私有或保护成员,它可以被声明为类的友元函数。
C++ 规定:
=、[]、()、->必须作成员函数
流运算符 << 和 >>、类转换运算符只能定义为友元般将单目运算符和复合运算符重载为成员函数般将双目运算符重载为友元函数
第 1个操作数非本对象的必须定义成友元函数