运算符重载:运算符重载只是一种”语法上的方便”,也就是它本质只是另一种函数调用的方式
运算符重载和其他函数一样也是一个函数,当编译器遇到适当的模式时,就会调用这个函数
定义重载的运算符就像定义函数,只是该函数的名字是operator@,这里的@代表了被重载的运算符。函数的参数中参数个数取决于两个因素
1运算符是一元(一个参数)的还是二元(两个参数);
2运算符被定义为全局函数(对于一元是一个参数,对于二元是两个参数)还是成员函数(对于一元没有参数,对于二元是一个参数-此时该类的对象用作左耳参数)
代码如下:
#include<iostream>
using namespace std;
class people
{
//重载<<运算符
friend ostream& operator<<(ostream& os, people& peo)
{
os << peo.age << endl;
return os;
}
public:
people()
{
age = 10;
}
people(int age)
{
this->age = age;
}
people(const people& peo)
{
cout << "拷贝构造" << endl;
age = peo.age;
}
//+运算符是两元运算符,在成员函数中重载只需要一元参数,类中本身对象也是一个参数
people operator+(people& peo)
{
people tem(peo.age + this->age);
return tem;
}
public:
int age;
};
#if 0
//在全局函数重载要两元参数
people operator+(people& peo1,people& peo2)
{
people tem(peo1.age+peo2.age);
return tem;
}
#endif
void test2()
{
people p1;
people p2;
people p3 = p1 + p2;//p3=p1.operator+(p2)
cout << p3 << endl;
}
int main()
{
//test();
test2();
system("pause");
return 0;
}
运算符重载成员函数和全局函数结果都一样
前置++和后置++的本质就是运算符重载
//重载前置++和后置++
class person
{
//重载<<运算符
friend ostream& operator<<(ostream& os, person& per)
{
os << per.age << endl;
return os;
}
public:
person()
{
age = 10;
}
//前置++
person& operator++()
{
age++;
return *this;
}
//后置++
person operator++(int)
{
person p2;
p2.age = this->age;
age++;
return p2;
}
public:
int age;
};
void test()
{
person per;
cout << "persn后置++等于" << per++ << endl;//返回的是临时对象,临时对象保存的是原来的值
cout << "persn前置++等于" << ++per << endl;//前面前置++对象加了1,在加上1就是2
}
int main()
{
test();
//test2();
system("pause");
return 0;
}
结果:
注意:从上面可以看出,能用前置++,就用前置++,因为前置++不用创建临时对象,效率高,除非需要后置++的原来的值
重载赋值运算符
赋值运算符重载很容易和拷贝构造函数混淆,在对象还没有初始化构造的时候,=调用的是拷贝构造函数,如果对象初始化后,=调用的是重载赋值运算符函数,默认赋值函数是进行简单的值拷贝(浅拷贝)
#include<iostream>
using namespace std;
class student
{
public:
student()
{
this->id = 10;
}
student(int id)
{
this->id = id;
}
student(const student& st)
{
cout << "拷贝构造" << endl;
this->id = st.id;
}
public:
int id;
};
void tsteS()
{
student Stu1(100);
student Stu2 = Stu1;//调用拷贝构造函数,Stu2没有初始化构造
cout << "Stu2="<<Stu2.id << endl;
cout << "----------------------" << endl;
student Stu3;
Stu3 = Stu1;//这个不调用拷贝构造,因为Stu3已经调用默认构造初始化了,所以调用的是默认赋值运算功能
//从结果可以看出默认赋值功能是进行简单的值拷贝,如果有指针的话就得重载赋值运算符的功能了
cout << "Stu3="<<Stu3.id << endl;
}
int main()
{
tsteS();
system("pause");
return 0;
}
结果如图:Stu3=Stu1并没有调用拷贝构造
赋值运算符的深拷贝
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//如果类的内部有指针,并且指针指向堆上的内存。
//默认把析构函数 赋值函数 拷贝构造函数都写上。
class Point
{
public:
Point(const char* name)
{
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
Point(const Point& p)
{
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, name);
}
//一定要返回引用类型,不然实现连续赋值的时候产生新的对象,赋值错误
Point& operator=(const Point& p)
{
//这个时候当前对象已经创建了,name已经指向了堆内存,直接赋值的话会导致堆内存没有及时释放
//释放原来的内存
if (name != NULL)
{
delete[] name;
this->name = NULL;
}
//开辟新的内存
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
return *this;
}
~Point()
{
if (name != NULL)
{
delete[] name;
this->name = NULL;
}
}
public:
char* name;
};
int main()
{
Point p1("make");
Point p2("peter");
Point p3("joke");
//(p3= p2) = p1;//如果不是返回值引用的话,p3就是p2的值,p3=p2产生新的对象,p1就给新的对象赋值了,没有赋值给p3,新的对象调用拷贝构造
p3 = p2 = p1;//如果不是引用的话,p2=p1产生新的对象,新的对象调用默认构造,赋值给p3就会出现值未知乱码
cout << p1.name << endl;
cout << p2.name << endl;
cout << p3.name << endl;
system("pause");
return 0;
}
如果赋值运算符函数返回值不是引用的话结果如下:
如果赋值运算符函数返回值是引用的话结果如下:
注意:除了赋值号(=)外,基类中被重载的操作符都将被派生类继承。