一.赋值运算符
1.运算符重载
(1) 运算符重载是具有特殊名字的函数,他的名字是由operator和后面要定义的运算符共同构成。和其他函数一样,它也具有其返回类型和参数列表以及函数体。
(2) 重载运算符函数的参数个数和该运算符作用的运算对象数量一样多。一元运算符有一个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。
(3) 如果⼀个重载运算符函数是成员函数,则它的第一个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少⼀个。
(4) 不能通过连接语法中没有的符号来创建新的操作符:比如operator@。
(5) .* :: sizeof ?: . 注意以上5个运算符不能重载。
(6) 重载操作符至少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义
(7) 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。 C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。
(8) 重载>>和<<时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第一个形参位 置,第一个形参位置是左侧运算对象, 调用时就变成了对象<<cout不符合使用习惯和可读性。 重载为全局函数把ostream/istream放到第一个形参位置就可以了,第二个形参位置当类类型对象。
int operator+(int x, int y)
{return x + y;
}
以上是一个错误的代码,通过这个代码也解释了第6条,必须有一个参数是类类型。
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}
这个才是正确的写法,其中的一个参数是类类型。
Date d1(2024, 10, 12);
Date d2(2024, 10, 13);
operator==(d1, d2);//第一种方式
d1 == d2;//第二种方式
这是对operator的调用,有两种方式。
class Date
{
public:bool operator==(const Date& d1){return d1._year == _year && d1._month == _month && d1._day == _day;}
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//private:int _year;int _month;int _day;
};
当我在类里面书写一个运算符重载时,当有两个参数,我只需要写一个即可,当有一个参数,我可以不写,因为第三条解释了,有一个隐含的this指针。
Date d1(2024, 10, 12);
Date d2(2024, 10, 13);
d1.operator==(d2);//第一种方法
d1 == d2;//第二种方法
当我写在类里面的时候,也是有两种方法,但是与上面的两种略有不同。
Date operator++(int)
{cout<<"后置++" << endl;Date tem = *this;_day++;return tem;
}
Date& operator++()
{cout << "前置++" << endl;_day++;return *this;
}
在后置++中加入的int形参可以不写参数名,因为我们并不需要传值,只是与前置++区分而已。
如果对这里的*this不太理解的话,可以看 C++----类和对象(一)-CSDN博客来了解。
Date d1(2024, 10, 13);
Date d2(2024, 10, 13);
Date tem1 = d1++;
Date tem2 = ++d2;
d1.Print();
tem1.Print();
d2.Print();
tem2.Print();
这是一个测试代码。
可以看到tem1是后置++,所以得到的是++之前的值,而tem2是前置++,所以得到的是++后的值。
如果我想写一个输出Date的年月日的重载运算符。
当我写在类里面时
void operator<<(ostream& out)
{out << _year <<"年"<< _month <<"月"<< _day<<"日" << endl;
}
那么代码就是以上的情况(不要忘记有隐藏的this指针)
但是当我要调用的时候,我写成这样
这样写是错误的,为什么呢?
因为这里已经进行了规定,当我写在类里面的时候,隐含了一个this,并且这个this在第一个参数的位置,根据第二条规定, 二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。所以我的左侧应该写一个Date类型的参数。
Date d1(2024, 10, 13);
d1 << cout;
所以这样才是正确的写法,但是这样写又太别扭了。那么我们应该怎么写呢?
这样我们只可以写在全局的函数了。
void operator<<(ostream& out,const Date& d1 )
{out << d1._year << "年" <<d1. _month << "月" <<d1. _day << "日" << endl;
}
但是这样存在一个问题
因为我们的成员都是private的类型,在外面访问不到,所以我们要先了解一个用法,以后我会详细的讲解。
friend void operator<<(ostream& out, const Date& d1);
将这个写在类的任意位置即可,这样我们便可以访问类里面的成员。
那么我想要连续的输出成员的年月日呢?
ostream& operator<<(ostream& out,const Date& d1)
{out << d1._year << "年" <<d1. _month << "月" <<d1. _day << "日" << endl;return out;
}
我们只要把他的返回值改变就可以。
因为返回值是ostream的流,所以当输出d1后,他们就会返回cout,变成cout<<d2,在输出d2。
注意:当调用ostream或者istream两个流的时候,我们一定要用引用。
2.赋值运算符
赋值运算符重载是一个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于⼀个对象拷贝初始化给另一个要创建的对象。
赋值运算符重载的特点:
1. 赋值运算符重载是一个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成 const当前类类型引用,否则会传值传参会有拷贝
2. 有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续赋值场景。
3. 没有显式实现时,编译器会自动生成⼀个默认赋值运算符重载,默认赋值运算符重载行为跟默认拷贝构造函数类似,对内置类型成员变量会完成值拷贝/浅拷贝(一个字节一个字节的拷贝),对自定义类型成员变量会调用他的赋值重载函数。
4. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的赋值运算符重载就可以完成需要的拷贝,所以不需要我们显示实现赋值运算符重载。像ST这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的赋值运算符重载完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像NEW这样的类型内部主要是自定义类型ST成员,编译器自动生成的赋值运算符重载会调用ST的赋值运算符重载, 也不需要我们显示实现NEW的赋值运算符重载。这里还有⼀个一技巧,如果⼀个类显示实现了析构并释放资源,那么他就需要显示写赋值运算符重载,否则就不需要。
代码的实现 :
void operator=(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}
这便是赋值运算符的应用。
Date d1(2024, 10, 13);
Date d2(2024, 10, 12);
d2 = d1;
d2.Print();
Date d3 = d1;
d3.Print();
这里有两个等号,但是意义不相同,第一个是赋值运算符,第二个是拷贝构造。
怎么区分呢?
拷贝构造是一个对象对另一个对象进行初始化,
而赋值重载是两个已经初始化好的对象进行拷贝。
Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;cout << "拷贝构造" << endl;
}
void operator=(const Date& d)
{cout << "这是赋值重载" << endl;_year = d._year;_month = d._month;_day = d._day;
}
以下是全部代码:
#include<iostream>
using namespace std;
class Date
{
public:bool operator==(const Date& d1){return d1._year == _year && d1._month == _month && d1._day == _day;}Date operator++(int){cout<<"后置++" << endl;Date tem = *this;_day++;return tem;}Date& operator++(){cout << "前置++" << endl;_day++;return *this;}void operator<<(ostream& out){out << _year <<"年"<< _month <<"月"<< _day<<"日" << endl;}friend ostream& operator<<(ostream& out, const Date& d1);void operator=(const Date& d){cout << "这是赋值重载" << endl;_year = d._year;_month = d._month;_day = d._day;}
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;cout << "拷贝构造" << endl;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& out,const Date& d1)
{out << d1._year << "年" <<d1. _month << "月" <<d1. _day << "日" << endl;return out;
}