目录
写一个简单的日期类:
- #pragma once;
- class Date
- {
- 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;
- };
- #include
- using namespace std;
- #include"Date.h"
-
- void TestDate()
- {
- Date d1;
- Date d2(2022, 9, 18);
- Date d3(2022, 2, 30);
- Date d4(2022, 2, 49);
- d3.Print();
- d4.Print();
- }
- int main()
- {
- TestDate();
- return 0;
- }
我们发现,日期d3和d4都不合法,我们进行运行:

也没有提示我们输入日期错误。
我们可以对我们的构造函数进行优化:
- Date(int year = 1, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- if (!(year >= 1 && month >= 1 && month <= 12 && day >= 1 && day <= GetMonthDay(year, month)))
- {
- cout << "非法日期" << endl;
- }
- }
日期类的拷贝构造:
对于日期类,我们不需要写对应的拷贝构造,因为对于内置类型,编译器会默认执行值拷贝:
- void TestDate()
- {
- Date d1;
- Date d2(2022, 9, 18);
- /*Date d3(2022, 2, 30);
- Date d4(2022, 2, 49);
- d3.Print();
- d4.Print();*/
- Date d5(d2);
- d5.Print();
- }
- int main()
- {
- TestDate();
- return 0;
- }


- void TestDate()
- {
- Date d1;
- Date d2(2022, 9, 18);
- /*Date d3(2022, 2, 30);
- Date d4(2022, 2, 49);
- d3.Print();
- d4.Print();*/
- /*Date d5(d2);
- d5.Print();*/
- Date d5;
- d5 = d2;
- }

拷贝构造:一个对象拷贝初始化另一个要创建的对象。
赋值重载:已经存在的两个对象之间的拷贝。


赋值重载可以传值传参吗?
可以,不会发生无穷递归,发生无穷递归的条件是: 拷贝构造时,使用传值传参,传值传参本身就是拷贝构造,拷贝构造又需要传值传参,无限循环。
所以这里可以传值传参。
但是传值传参还需要调用拷贝构造,有些麻烦,我们最好还是使用传地址传参。

- void operator=(const Date&d)
- {
- _year = d._year;
- _month = d._month;
- _day = d._day;
- }
- void TestDate()
- {
- Date d1(2022, 9, 10);
- Date d2;
- d2 = d1;
- }
- int main()
- {
- TestDate();
- return 0;
- }
我们进行测试:

赋值成功。
赋值重载写的不够全面,我们知道整型可以连等:
- int main()
- {
- /*TestDate();*/
- int i, j;
- i = j = 1;
- return 0;
- }
我们这里是不能连等的:
- void TestDate()
- {
- Date d1(2022, 9, 10);
- Date d2, d3;
- d2.Print();
- d3=d2 = d1;
- d2.Print();
- }
- int main()
- {
- TestDate();
- /*int i, j;
- i = j = 1;*/
- return 0;
- }

原因如下:


这里我们把d1赋值给d2之后,返回值应该是d2,但是我们实现的函数是没有返回值的,所以我们需要完善返回值:
- Date operator=(const Date&d)
- {
- _year = d._year;
- _month = d._month;
- _day = d._day;
- return *this;
- }
传值传参是拷贝构造,传值返回也是拷贝构造,所以我们这里可以用传引用返回:
而且this指向的对象出了作用域依旧存在,我们可以用引用返回。
能不能不返回*this,返回d呢?
答:不可以
- Date& operator=(const Date&d)
- {
- _year = d._year;
- _month = d._month;
- _day = d._day;
- return d;
- }
1:因为d是只读的,传引用返回后的类型变成可读可写的了,属于权限的放大。
2:赋值的返回值是左操作数,不能返回右操作数。

赋值运算符的重载对于内置类型完成值拷贝,所以对于日期类,我们可以不定义赋值重载

我们没有定义赋值重载函数,依旧可以完成日期类对象的赋值。
大部分类的默认赋值重载都不需要我们自定义,但是有些需要,例如栈:
- #pragma once;
- class Stack
- {
- public:
- Stack(int capacity=4 )
- {
- _a = (int*)malloc(sizeof(int)*capacity);
- if (_a == nullptr)
- {
- perror("malloc fail");
- exit(-1);
- }
- _top = 0;
- _capacity = capacity;
- cout << "Stack构造函数()" << endl;
- }
- Stack(const Stack&st)
- {
- _a=(int*)malloc(sizeof(int)*st._capacity);
- if (_a == nullptr)
- {
- perror("malloc fail");
- exit(-1);
- }
- memcpy(_a, st._a, sizeof(int)*st._top);
- _top = st._top;
- _capacity = st._capacity;
- }
- void Push(int x)
- {
- _a[_top++] = x;
- }
- ~Stack()
- {
- free(_a);
- _a = nullptr;
- _top = _capacity = 0;
- cout << "Stack析构函数()" << endl;
- }
- private:
- int*_a;
- int _capacity;
- int _top;
- };
我们没有实现赋值重载,看看能否使用赋值重载
- void StackTest()
- {
- Stack st1;
- st1.Push(1);
- st1.Push(2);
-
- Stack st2;
- st2.Push(10);
- st2.Push(20);
- st2.Push(30);
- st2.Push(40);
- st1 = st2;
- }
- int main()
- {
- StackTest();
- /*int i, j;
- i = j = 1;*/
- return 0;
- }

运行之后,直接报错。

我们进行值拷贝:
把st2._top赋值给st1._top,把st2._capacity赋值给st1._capacity。
再让st1._a指向st2._a的位置。

后创建st2的先析构,析构之后st2所指向的空间的数据释放了,这时候st1再进行析构就导致了越界访问。
并且原本属于st1的指向的数据的地址也找不到了,造成了内存泄漏。

所以对于栈类,我们要自己实现赋值重载:

我们先释放掉st1所指向的空间的数据,然后新创建一个空间,拷贝st2的数据,让st1指向该空间:

- Stack& operator=(const Stack&st)
- {
- free(_a);
- _a = (int*)malloc(sizeof(int)*st._capacity);
- if (_a == nullptr)
- {
- perror("malloc fail");
- exit(-1);
- }
- memcpy(_a, st._a, sizeof(int)*st._top);
- _top = st._top;
- _capacity = st._capacity;
- return *this;
- }

完成了深拷贝。
还有一种情况:

用自己去赋值自己。

我们进行运行,发现数据出现了错误。
因为
我们可以先提前判断:
- Stack& operator=(const Stack&st)
- {
- if (&st != this)
- {
- free(_a);
- _a = (int*)malloc(sizeof(int)*st._capacity);
- if (_a == nullptr)
- {
- perror("malloc fail");
- exit(-1);
- }
- memcpy(_a, st._a, sizeof(int)*st._top);
- _top = st._top;
- _capacity = st._capacity;
- }
- return *this;
- }
对于一些有资源释放的类,我们也可以不自己写:
- class MyQueue
- {
- public:
- void push(int x)
- {
- _pushST.Push(x);
- }
- private:
- Stack _pushST;
- Stack _popST;
- size_t size = 0;
- };
对于内置类型完成值拷贝,对于自定义类型,调用其默认赋值重载。
- void MyQueueTest()
- {
- MyQueue s1;
- s1.push(10);
- MyQueue s2;
- s2 = s1;
- }
- int main()
- {
- MyQueueTest();
- /*int i, j;
- i = j = 1;*/
- return 0;
- }

完成了深拷贝。
对于比较小并且频繁调用的函数,这类函数一般是内联函数,内联函数只能在类里面定义,不能声明和定义分离。
对于行数不多并且调用不频繁的我们可以声明和定义分离。
- Date&Date::operator-=(int day)
- {
- _day -= day;
- while (_day <= 0)
- {
- _month--;
- if (_month == 0)
- {
- _year--;
- }
- _day += GetMonthDay(_year, _month);
- }
- return *this;
- }
- #include"Date.h"
- void TestDate1()
- {
- Date d1(2022, 10, 19);
- d1 -= 1000;
- d1.Print();
- }
- int main()
- {
- TestDate1();
- return 0;
- }
![]()
- Date Date::operator-(int day)
- {
- Date ret(*this);
- ret -=day;
- return ret;
- }
- #include"Date.h"
- void TestDate1()
- {
- Date d1(2022, 10, 19);
- Date d2 = d1 - 10000;
- d2.Print();
- }
- int main()
- {
- TestDate1();
- return 0;
- }

假如我们要减去一个负数呢?
- #include"Date.h"
- void TestDate1()
- {
- Date d1(2022, 10, 19);
-
- d1 -= -10000;
- d1.Print();
- }
- int main()
- {
- TestDate1();
- return 0;
- }

我们在定义的时候如何区分前置和后置++?

- Date& Date::operator++()
- {
- *this += 1;
- return *this;
- }
- Date Date::operator++(int)
- {
- Date ret = *this;
- *this + 1;
- return ret;
- }
