重载运算符:
接上回的重载运算符:
1.如果一个重载运算符函数是成员函数,则他的第一个运算对象默认传给隐式的this指针,所以运算符重载作为成员函数时,参数比运算对象少一个。
2.运算符重载之后其优先级和结核性跟对应的内置类型一致。
3. .* :: sizeof() ?: . 这五种运算符不能重载。
对于第一个.*运算符
4.重载运算符至少要有一个类类型的参数,不能通过运算符重载改变内置类型对象的含义。
5.一个类需要哪些运算符是要看哪些运算符重载后有意义。
6.重载++或者--运算符时,有前置的和后置的,但是其运算重载函数的函数名都是operator++或者operator--所以c++规定对于后置的++--,增加一个int形参,跟前置的构成函数重载,得以区分。 在使用前置跟后置的++--时跟内置类型的使用一摸一样。
赋值运算符的重载
赋值运算符重载是一个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,注意跟拷贝构造区分,拷贝构造是用于将一个已经存在的对象的值直接拷贝到一个要创建的对象。
1.赋值运算符是一个运算符重载函数,规定必须重载为成员函数。赋值运算符的参数建议写成const当前类类型的引用,否则传值传参会有拷贝。
2.有返回值,建议写成当前类类型的引用,引用返回是为了提高效率,有返回值是为了支持连续赋值场景。
3.没有显示的写赋值重载运算符时,系统会自动生成一个默认的赋值重载运算符。与拷贝构造类似,赋值重载运算符也只是浅拷贝,如果有指向的资源开辟的空间等需要深拷贝的情况需要自己写赋值重载运算符函数。
代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1,int day=1)
{
_year = year;
_month = month;
_day = day;
}
bool operator<(const Date& d2)//但是当我们将运算符重载函数变成成员函数之后,他会默认带有this指针,所以传参会少一个。
{
if (_year < d2._year)
return true;
else if (_year == d2._year &&
_month < d2._month)
return true;
else if (_year == d2._year &&
_month == d2._month &&
_day < d2._day)
return true;
return false;
}
int getmonthday(int year, int month)//后的某年某月的天数
{
assert(month > 0 && month < 13);
static int monthdayarray[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
return 29;
else
return monthdayarray[month];
}
Date operator+(int day)//日期加天数返回日期
{
_day += day;
while (_day > getmonthday(_year, _month))
{
_day -= getmonthday(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month -= 12;
}
}
return *this;
}
Date operator++(int i)
{
Date tmp = *this;//调用拷贝构造
*this + 1;
return tmp;//这里出了函数会销毁tmp所以使用传值返回,返回的是tmp的一个拷贝。
}
Date& operator++()
{
*this + 1;
return *this;//这里this出了函数还是存在的所以可以使用引用返回
}
Date& operator=(const Date& d)//这是赋值重载运算符函数 一定注意这里的引用传参跟引用返回,就是为了避免调用拷贝构造,减少拷贝,提高效率
{
//注意这里可能会自己给自己拷贝。看似好像拷贝了不会出现什么问题,只是效率降低了,但是在深拷贝时自己给自己拷贝就有问题了。指向了两个空间时不可以的
if (this != &d)
{
_day = d._day;
_month = d._month;
_year = d._year;
return *this;
}
return *this;
}
void print()
{
cout << _year << '/' << _month << '/' << _day << endl;
}
//int func(int a,int b)
//{
// return a + b;
//}
private:
int _year;
int _month;
int _day;
};
//typedef int(Data::*pf)(int,int);//这是将class Data中成员函数为int XXXX(int,int)的函数的指针类型定义成了pf。
//int add(int a, int b)
//{
// return a + b;
//}
//typedef int(*p)(int, int);//这里是将类型为int XXX(int,int)的函数指针类型定义成p;
int main()
{
//pf pfunc= &Data::func;//这是将func函数的指针赋值给pfunc。
Date d1(2024,8,31);
Date d2 = d1;//注意这是拷贝构造。
d2 = d2;
d2.print();
Date d3(2024,1,1);//这里是构造函数
d3 = d1;//注意这里是复制运算符的重载
d3.print();
putchar('\n');
//cout << (d1.*pfunc)(1, 2) << endl;//这是对d1中func函数的调用
//p padd = &add;
//cout << (*padd)(1, 2) << endl;
(d1.operator++(1)).print();//跟下面等价
(d1++).print();
d1.print();
(++d1).print();
return 0;
}
总结:
1.构造一般都需要自己写,自己传参定义初始化。
2.构造时有资源的申请如malloc。calloc,fopen等等,就需要显示的写析构函数(就是你是有空间需要释放的)但是如果是你的成员变量是类或者结构体,他自己是应该有自己的析构函数的,如果这个类或结构体中没有指向的资源,就不用写析构函数,在调用默认的析构函数时会调用对应的类的或结构体的析构函数。
3.拷贝构造跟赋值重载是类似的,如果要写析构(有资源的调用或指向)那么就要实现深拷贝,就要写拷贝构造跟赋值重载。
4.是的,就是这么简单(连我这种蒟蒻都能理解你肯定没问题)各自管理好各自的,不需要越级管理,我的附庸的附庸不是我的附庸,我只能管理好我的成员变量,对于成员内部的事是归相应的成员变量管。当你管理好你的下级,你的下级自然会管理好你下级的下级。(当然前提是你得写好各个上下级之间的函数)