运算符重载
、概念:对已有的运算符重新进行定义,赋予另一种功能以适应不同的数据类型
1、加号运算符重载
作用:实现两个自定义数据类型相加的运算
下面是为了实现Person类的两个对象相加,赋值给p3
方法一:通过成员函数重载
重载函数:
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;
}
调用:
Person p3 = p1.operator+ (p2)
//简化为:
Person p3 = p1+p2;
方法二:通过全局函数重载
重载函数:
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 p3 = operator+ (p1,p2)
//简化为:
Person p3 = p1+p2;
只能对自定义数据类型的表达式的运算符进行重载
对于内置的数据类型的表达式的运算符不可能改变
不要滥用运算符重载
运算符重载也可以发生函数重载
2、左移运算符重载<<
作用:输出自定义数据类型
下面是为了实现直接输出Person类的一个对象
即输出 成员的对应值
方法一:通过成员函数重载
不使用,因为简写后格式为 p<<cout,不符合cout在左边的规则
方法二:通过全局函数重载
重载函数:
void operstor<<(ostream &cout,Person &p){
//cout是ostream数据类型,标准输出流数据类型
//因为只有一个cout,所以要用传引用
cout<<"m_A="<<p.m_A<<" m_B="<<p.m_B;
}
调用:
operator<< (cout,p);
//简化为
cout<<p;
!注意!
这里cout<<p后面如果加<<endl会报错,因为operator<<返回值是void,在cout<<p后面无法链式编程
想要链式编程输出需要把operator<<函数的返回值改为ostream &
3、递增运算符重载++
作用:通过重载自增运算符,实现自己的整型数据
补充 i++和 ++i
int a=10;
cout<<++i<<endl;//输出11.先自增后运行该表达式
cout<<i<<endl;//输出11
cout<<i++<<endl;//输出10.先运行该表达式后自增
cout<<i<<endl;//输出11
自定义整型变量
class MyInteger{
public:
MyInteger(){
m_Num=0;
}
private:
int m_Num;
};
为了一会儿可以直接输出自定义的数据类型先进行左移运算符的重载
ostrean& operator<<(ostream & cout,MyInteger myint){
cout<<myint.m_Num;
return cout;
}
为了让operator<<函数可以访问MyInteger的私有成员,要在MyInteger中添加友元frend:ostrean& operator<<(ostream & cout,MyInteger myint);
重置前置++运算符
//这里的返回值是值,不是引用
//因为返回的是局部对象,局部对象返回之后立即释放,如果返回值类型写&会报错
MyInteger operator++(){
MyInteger temp = *this;
m_Num++;
return temp;
}
重置后置++运算符
//这里参数写int是因为不写就会报错,不写int后置重载函数和前置重载函数只有返回值不同,而只有返回值不同不能函数重载
//int在这里代表占位参数,可以用于区分前置和后置递增
MyInteger& operator++(int){
//这个返回值用传引用是因为自增永远是在对变量本身做加法
m_Num++;
return *this;//解引用
}
4、赋值运算符重载=
补充:
c++编译器至少给一个类添加四个函数
1、默认构造函数(无参,函数体为空)
2、默认析构函数(无参,函数体为空)
3、默认拷贝构造函数(对属性进行值拷贝)
4、赋值运算符 operator=,对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题
创建一个类
class Person{
public:
Person(int age){
m_Age = new int(age);//把传入的参数保存在新建的堆区中,并且让指针m_Age指向这个堆区
}
//堆区由程序员自己建立自己释放所以要编写析构函数
~Person(){
if(m_Age != NULL){
delete m_Age;
m_Age = NULL;
}
}
int *m_Age;
};
补充
这个类的成员属性直接通过创建对象输出cout<<p1.m_Age<<endl;
输出的是乱码,如果要输出值需要解引用
cout<<*p1.m_Age<<endl;
赋值运算符重载的原因
当直接用对象1给对象2赋值时是浅拷贝,由于指向同一个堆区,会导致析构函数重复释放一个堆区,程序崩溃,所以要进行深拷贝
//在Person类里面重载赋值运算符
//返回值类型是为了链接编程(例如p3=p2=p1)
//返回值是&引用是因为返回它本身
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;
}
5、关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
创建一个Person类
class Person{
public:
Person(string name,int age){
m_Name = name;
m_Age = age;
}
string m_Name;
int m_Age;
};
关系运算符重载
在Person类里面重载关系运算符
bool operator==(Person & p){
if(this->m_Name==p.m_Name&&this->m_Age==p.m_Age){
return true;
}
return false;
}
此时就可以直接用==比较Person类的对象p1和p2
6、函数调用运算符重载()
由于重载后使用的方式非常像函数调用,因此又被称为仿函数
仿函数没有固定写法,很灵活
//重载()
class MyPrint{
public:
void operator()(string test){
cout<<test<<endl;
}
};
void MyPrint2(string test){
cout<<test<<endl;
}
//调用重载的operator()函数
void test(){
MyPrint mp;
mp("hello worfd!");//调用重载的operator()函数
}
//调用MyPrint2函数
void test2(){
MyPrint2("hello world!");//调用全局函数
}
匿名对象
cout<<MyPrint()(“hello world!”)<<endl;
匿名对象使用完立马释放