运算符重载
运算符重载有两种方式
- 使用类的成员函数
- 使用全局函数
内置类型运算符的含义是不可更改的
+ 运算符的重载
可以使用上面
class Person{
public:
int age = 0;
Person(){};
Person(int age):age(age){};
//使用类的成员函数进行运算符重载
Person operator+(Person& p){
Person tmp;
tmp.age = this->age + p.age;
return tmp;
}
};
//使用全局函数进行运算符重载
Person operator-(Person& p1,Person& p2){
Person tmp;
tmp.age = p1.age - p2.age;
return tmp;
}
//运算符重载作为函数,同样可以提供多个版本
Person operator-(Person& p,int a){
Person tmp;
tmp.age = p.age - a;
return tmp;
}
int main(){
Person p1(10);
Person p2(20);
Person p3 = p2 + p1;
Person p4 = p2 - p1;
cout << p3.age << endl; //30
cout << p4.age << endl; //10
}
>> 运算符重载
// << 运算符重载不能作为成员函数,第一个参数必须是输出流
ostream& operator<<(ostream& out, Person& p){
out << "p.age=" << p.age;
return out;
}
int main(){
cout << p3 << endl; //p.age=30
}
前置和后置递增递减运算符重载 ++ 为例
class Person{
public:
int age = 0;
Person(){};
Person(int age):age(age){};
Person& operator++(){ //前置运算符
++this->age;
return *this;
};
Person operator++(int){ //后置运算符
Person tmp = *this; //创建了一个临时对象
this->age++;
return tmp;
}
};
1:前置运算符返回的是引用,传入参数没有占位符
2:后置运算符用了一个占位符,返回的是值
后置运算符由于创建了一个中间变量,所以效率不如前置
那么,前置++为什么要返回引用?看下面这个例子
Person p; //假如p的前置++运算符重载返回的不是引用
cout << ++(++p) << endl;
cout << p.age << endl; //输出是1
之所以会这样,是因为第一次++返回的已经不是原先的对象了,再次++,是对新的对象+1
后置++为什么不返回引用呢?
后置返回的是一个临时变量,临时变量在返回后会被销毁,如果是引用,那么引用了一个被销毁的变量,会出现野指针异常。
指针运算符重载(智能指针)
在c++中,使用new申请的内存是需要手动释放的,但许多时候会记不得~
于是乎,就有了智能指针,帮你释放内存
当智能指正被析构,所指向的对象也会被析构
使用 -> 和 * 的重载我们就可以实现智能指针的功能
template<typename T>
class smartPoint{
T* content;
public:
smartPoint(T* content):content(content){};
T* operator->(){
return this->content;
}
//重载解引用运算符
T& operator*(){
return *(this->content);
}
//指针被释放的时候,内部的content所指向的对象也被释放了
~smartPoint(){
if (this->content != NULL){
delete this->content;
this->content = NULL;
}
}
};
class Person{
public:
int age = 10;
void say(){
cout << "hello world" << endl;
}
};
int main(){
smartPoint<Person> s(new Person);
cout << s->age << endl; //这里按理来说,应该是 ->->,第一个->返回Person*,第二个->用于取值,编译器帮我们优化成一个了。
(*s).say();
}
赋值运算符 = 重载
c++默认也会重载 = 运算符,但是对于指针类型,只会对指针本身进行拷贝,这样析构函数时,会对指针指向的堆区内存重复释放,造成错误,这个时候就需要我们重载 = 运算符。
class Person {
public:
Dog* dog;
Person(string name) {
this->dog = new Dog(name);
};
Person& operator=(const Person& p) {
if (this->dog != NULL) {
delete this->dog;
}
this->dog = new Dog(p.dog->name);
return *this;
}
~Person() {
if (this->dog != NULL) {
delete this->dog;
this->dog = NULL;
}
}
};
这里就要吐槽一下clion了,重复delete一块堆区内存居然不报错!!!,还有,visual studio代码编辑器能做的好用些么???
索引运算符重载 [ ]
int& operator[](int index){
return this->array[index];
}
这里需要注意的就是要返回引用,只有引用才能定位到数组要赋值的那块内存。
比较运算符重载
class Person{
string name;
int age;
public:
Person(string name,int age):name(name),age(age){};
bool operator==(Person& p){
if(p.age == this->age && p.name == this->name){
return true;
} else{
return false;
}
}
bool operator!=(Person& p){
if(p.age == this->age && p.name == this->name){
return false;
} else{
return true;
}
}
};
函数运算符 () 重载
class Person{
public:
void operator()(string str){
cout << str << endl;
}
};
int main(){
Person()("hello world");
}
这里使用了匿名对象,匿名对象调用了函数运算符,执行了一个仿函数。函数运算符的重载很灵活,可以定义自己要传入的参数和返回值。
与 && 或 || , 运算符不要重载
运算符本身有短路特性,重载后无法实现这些特性。