1.类的6个默认成员函数如果一个类中什么成员都没有简称为空类。空类中真的什么都没有吗并不是任何类在什么都不写时编译器会自动生成以下6个默认成员函数。默认成员函数用户没有显式实现编译器会生成的成员函数称为默认成员函数。2.构造函数2.1概念对于Date类可以通过Init公有方法给对象设置日期但如果每次创建对象时都调用该方法设置信息未免有点麻烦那能否在对象创建时就将信息设置进去呢构造函数是一个特殊的成员函数名字与类名相同创建类类型对象时由编译器自动调用以保证每个数据成员都有一个合适的初始值并且在对象整个生命周期内只调用一次。2.2 特性构造函数是特殊的成员函数需要注意的是构造函数虽然名称叫构造但是构造函数的主要任务并不是开空间创建对象而是初始化对象。其特征如下:1.函数名与类名相同。2.无返回值。3.对象实例化时编译器自动调用对应的构造函数。4.构造函数可以重载。#includeiostream using namespace std; class Date { public: //构造函数在对象构造时调用的函数 //这个函数完成初始化工作 Date(int year, int month, int day) { _year year; _month month; _day day; } void Print() { cout _year -; cout _month -; cout _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 9); d1.Print(); return 0; }#includeiostream using namespace std; class Date { public: //构造函数在对象构造时调用的函数 //这个函数完成初始化工作 Date(int year, int month, int day) { _year year; _month month; _day day; } //构造函数可以重载 Date() { _year 0; _month 0; _day 0; } void Print() { cout _year -; cout _month -; cout _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 9); d1.Print(); //Date d2();//错 Date d2; d2.Print(); //0-0-0 return 0; }5.如果类中没有显式定义构造函数则C编译器会自动生成一个无参的默认构造函数一旦用户显式定义编译器将不再生成。#includeiostream using namespace std; class Date { public: //没有显式定义构造函数 // 这里编译器生成无参默认构造函数 void Print() { cout _year -; cout _month -; cout _day endl; } private: int _year; int _month; int _day; }; int main() { //调用编译器生成构造函数 Date d1; d1.Print(); //得到随机数 return 0; }我们没有显式定义构造函数这里编译器生成无参默认构造函数默认生成无参构造函数语法坑双标狗1针对内置类型的成员变量没有做处理2针对自定义类型的成员变量调用它的构造函数初始化#includeiostream using namespace std; class Time { public: Time() { _hour 0; _minute 0; _second 0; } private: int _hour; int _minute; int _second; }; class Date { public: void Print() { cout _year -; cout _month -; cout _day endl; } private: int _year; int _month; int _day; Time _t; }; int main() { return 0; }一旦用户显式定义编译器将不再生成。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Time { public: Time() { _hour 0; _minute 0; _second 0; } private: int _hour; int _minute; int _second; }; class Date { public: //一旦用户显式定义编译器将不再生成。 Date(int year, int month, int day) { _year year; _month month; _day day; } void Print() { cout _year -; cout _month -; cout _day endl; } private: int _year; int _month; int _day; Time _t; }; int main() { //无法调用。 Date d1; d1.Print(); return 0; }再多写一个无参的构造函数即可正常调用。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Time { public: Time() { _hour 0; _minute 0; _second 0; } private: int _hour; int _minute; int _second; }; class Date { public: //写一个无参的构造函数 Date() { _year 0; _month 1; _day 1; } Date(int year, int month, int day) { _year year; _month month; _day day; } void Print() { cout _year -; cout _month -; cout _day endl; } private: int _year; int _month; int _day; Time _t; }; int main() { //就能正常调用了 Date d1; d1.Print(); return 0; }那有没有更好的方式呢答全缺省。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Time { public: Time() { _hour 0; _minute 0; _second 0; } private: int _hour; int _minute; int _second; }; class Date { public: //更灵活的方式——全缺省 Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } void Print() { cout _year -; cout _month -; cout _day endl; } private: int _year; int _month; int _day; Time _t; }; int main() { Date d1; d1.Print(); Date d2(2026, 1, 20); d2.Print(); return 0; }6.无参的构造函数和全缺省的构造函数都称为默认构造函数并且默认构造函数只能有一个。但如果全缺省和无参同时存在就会存在歧义。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Time { public: Time() { _hour 0; _minute 0; _second 0; } private: int _hour; int _minute; int _second; }; class Date { public: //但如果无参和全缺省同时存在就会有歧义 Date() { _year 0; _month 1; _day 1; } Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } void Print() { cout _year -; cout _month -; cout _day endl; } private: int _year; int _month; int _day; Time _t; }; int main() { //d1这里会报错。 Date d1; d1.Print(); Date d2(2026, 1, 20); d2.Print(); return 0; }默认构造函数1自己实现无参的构造函数2自己实现的全缺省构造函数3我们不写编译器自动生成默认构造函数特点不用传参数7.关于编译器生成的默认成员函数在我们不实现构造函数的情况下编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用d对象调用了编译器生成的默认构造函数但是对象year/month/day依旧是随机值。也就说在这里编译器生成的默认构造函数并没用解答C把类型分成内置类型基本类型和自定义类型。内置类型就是语法已经定义好的类型如int/char自定义类型就是我们使用class/struct/union自己定义的类型看看下面的程序就会发现编译器生成默认的构造函数会对自定类型成员t调用的它的默认成员函数。3.析构函数析构函数与构造函数功能相反析构函数不是完成对象的销毁局部对象销毁工作是由编译器完成的。对象在销毁时会自动调用析构函数完成类的一些资源清理工作。析构函数是特殊的成员函数。其特征如下1.析构函数名是在类名前加上字符一。2.无参数无返回值。3.一个类有且只有一个析构函数。若未显式定义系统会自动生成默认的析构函数。4.对象生命周期结束时C编译系统系统自动调用析构函数。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { cout Date() endl; _year year; _month month; _day day; } void Print() { cout _year -; cout _month -; cout _day endl; } ~Date() { cout ~Date() endl; } private: int _year; int _month; int _day; }; int main() { Date d1; Date d2; return 0; }4.拷贝构造1拷贝构造函数是构造函数的一个重载形式。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Date { public: Date(int year 0, 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; }; int main() { Date d1(2026, 1, 21); Date d2(2026, 1, 21); //拷贝d1 Date d3(d1);//拷贝构造 d1.Print(); d2.Print(); d3.Print(); return 0; }错误示范无限递归#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } //这是错的会无限递归 Date(Date d) { _year d._year; _month d._month; _day d._day; } private: int _year; int _month; int _day; }; int main() { return 0; }调用的就是拷贝构造但是在调用函数时要先传参数传参的过程又是一个拷贝构造的调用那怎么解决这个问题呢答引用。2拷贝构造函数的参数只有一个且必须使用引用传参使用传值方式会引发无穷递归调用。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } void Print() { cout _year . _month . _day endl; } //引用 Date(Date d) { _year d._year; _month d._month; _day d._day; } private: int _year; int _month; int _day; }; int main() { return 0; }这时d是d1的别名没有拷贝构造#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } void Print() { cout _year . _month . _day endl; } Date(Date d) { _year d._year; _month d._month; _day d._day; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 21); //这两种写法都是拷贝构造 Date d2(d1); Date d3 d1; //打印出来效果一样 d1.Print(); d2.Print(); d3.Print(); return 0; }【拓】加const更安全如果赋值等号两边写反了会报错。3若未显示定义系统生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝我们叫做浅拷贝或者值拷贝。我们不实现时编译器生成拷贝构造和operator会完成按字节的值拷贝浅拷贝也就是说有些类我们是不需要去实现拷贝构造和operator的因为编译器默认生成就可以用。比如Date就是这样但是Stack就必须自己写拷贝不然会造成浅拷贝问题。浅拷贝单纯拷贝值。如图st2析构释放了一次_a的空间st1析构又释放一次。而同一块空间不能被释放两次所以同一块空间释放两次会导致崩溃。所以不能单纯拷贝值st2还要开辟和st1一块一样大的空间。4自己实现深拷贝和operator。5.赋值运算符重载5.1运算符重载C为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有其返回值类型。函数名字以及参数列表其返回值类型与参数列表与普通的函数类似。函数名字为关键字operator后面接需要重载的运算符符号。函数原型返回值类型operator操作符(参数列表注意:●不能通过连接其他符号来创建新的操作符比如operator●重载操作符必须有一个类类型或者枚举类型的操作数●用于内置类型的操作符其含义不能改变例如内置的整型不能改变其含义●作为类成员的重载函数时其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this限定为第一个形参●*、、sizeof、、注意以上5个运算符不能重载。这个经常在笔试选择题中出现。自定义类型是不能用运算符的要用就得实现重载函数自定义类型用的时候等价于调用这个重载函数。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } void Print() { cout _year . _month . _day endl; } Date(const Date d) { _year d._year; _month d._month; _day d._day; } //要把私有改成公有 //不然编译会报错 //private: int _year; int _month; int _day; }; //运算符有几个参数 //operaor重载的函数就有几个参数 bool operator(const Date d1, const Date d2) { return d1._year d2._year d1._month d2._month d1._day d2._day; } int main() { Date d1(2026, 1, 21); Date d2(2026, 1, 22); d1 d2; //等价于 operator(d1, d2); return 0; }一般写成内置函数。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } void Print() { cout _year . _month . _day endl; } Date(const Date d) { _year d._year; _month d._month; _day d._day; } //d1d2 //d1.operator(d1,d2) //有隐含的this指针 //bool operator(Date* this,const Date d) bool operator(const Date d) { return _year d._year _month d._month _day d._day; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 21); Date d2(2026, 1, 22); d1 d2; //d1.operator(d2); return 0; }但是我们一般不会这样写因为这样可读性不好。对比赋值运算符重载和拷贝构造#includeiostream using namespace std; class Date { public: Date(int day, int month, int year) { _day day; _month month; _year year; } //1.出了作用域对象还在 //就使用引用返回 //能减少一次拷贝 //2.加上返回类型后可以实现连等 //如d1d2d3 Date operator(const Date d) { if (this ! d) { _year d._year; _month d._month; _day d._day; } return *this; } void Print() { cout _year - _month - _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1(1, 1, 1); Date d2(0, 0, 0); //1.两个对象都已经存在并初始化好了 //现在把d2复制拷贝给d1 d2 d1; //2.d3还不存在 //构造d3时用d1去初始化 Date d3(d1); //3.特别注意这是拷贝构造 //不是operator Date d4 d1; return 0; }6.实现一个完善的日期类公转转一圈是一年自转一圈是一天一年公转一圈自球自转365圈每年365天大概多出5h没过4年多出接近24-h每过100年这一天又没了每过400年这一天又有这一天1)实现运算符重载、、、、#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Date { public: int GetMonthDay(int year, int month) { //为防止每次进来都判断创建一次数组 //而且数组不变所以用static修饰 static int monthDays[13] { 0,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; return monthDays[month]; } Date(int year 0, int month 1, int day 1) { //为防止非法日期要先判断 if (year 0 month 1 month 12 day 1 day GetMonthDay(year, month)) { _year year; _month month; _day day; } else cout fail date endl; } Date(const Date d) { _year d._year; _month d._month; _day d._day; } bool operator(const Date d) { if (_year d._year) return true; else if (_year d._year _month d._month) return true; else if (_year d._year _month d._month _day d._day) return true; return false; } bool operator(const Date d) { return _year d._year _month d._month _day d._day; } //d1d2 //d1.operator(d1,d2); //bool operator(Date* this,const Date d) //其中: //d1-this //d2-d bool operator(const Date d) { //复用上面的函数来实现 // d1d2 || d1d2 return *this d || *this d; } bool operator(const Date d) { //d1d2 //!(d1 d2) return !(*this d); } void Print() { cout _year - _month - _day; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 21); Date d2(2025, 2, 2); cout (d1 d2) endl;//0 return 0; }2)日期的加减乘除#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class Date { public: int GetMonthDay(int year, int month) { //为防止每次进来都判断创建一次数组 //而且数组不变所以用static修饰 static int monthDays[13] { 0,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; return monthDays[month]; } Date(int year 0, int month 1, int day 1) { //为防止非法日期要先判断 if (year 0 month 1 month 12 day 1 day GetMonthDay(year, month)) { _year year; _month month; _day day; } else cout fail date endl; } Date(const Date d) { _year d._year; _month d._month; _day d._day; } //日期加一个天数 //Date operator(int day) //{ // //用d1拷贝构造一个ret // Date ret(*this); // ret._day day; // while (ret._day GetMonthDay(ret._year, ret._month)) // { // //如果日期的天数不合法就需要进位到月 // ret._day - GetMonthDay(ret._year, ret._month); // ret._month; // if (ret._month 13) // { // ret._year; // ret._month 1; // } // } // return ret; // //出了作用域ret就销毁了所以普通返回即可 //} //和的区别要改变自身 Date operator(int day) { if (day 0) return *this - -day; _day day; while (_day GetMonthDay(_year, _month)) { _day - GetMonthDay(_year, _month); _month; if (_month 13) { _year; _month 1; } } return *this; //出了作用域*this还在所以引用返回 } //复用 Date operator(int day) { Date ret(*this); ret day; return ret; } //d1 Date operator() { *this 1; return *this; } //d1 //为了构成函数重载这里多加一个参数 Date operator(int) { Date tmp(*this); *this 1; //返回加之前的值 return tmp; } //d1-10 Date operator-(int day) { _day - day; while (_day 0) { _month--; if (_month 0) { _year--; _month 12; } _day GetMonthDay(_year, _month); } return *this; } //d1-10 Date operator-(int day) { Date ret *this; ret - day; return ret; } //--d1 Date operator--() { *this - 1; return *this; } //d1-- Date operator - (int) { Date tmp(*this); *this - 1; return tmp; } inline bool operator(const Date d) { if (_year d._year) return true; else if (_year d._year _month d._month) return true; else if (_year d._year _month d._month _day d._day) return true; return false; } //d1-d2 int operator-(const Date d) { int flag 1; Date max *this; Date min d; if (*this d) { max d; min *this; flag -1; } int n 0; while (min max) { //能调用前置就调用前置 // 因为后置多了一个拷贝构造 min; n; } return n * flag; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 21); Date d2 d1 100; d2.Print(); //2026.5.1 return 0; }7.const成员1.对象调const成员函数const Date*p1-*p1 指向的对象Date const *p2 -*p2 指向的对象Date* const p3-p3 指针本身#includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } //void Print(const Date* this) void Print() const { cout _year - _month - _day; } private: int _year; int _month; int _day; }; void f(const Date d) { d.Print(); } int main() { return 0; }2.成员函数调用const成员函数class Date { public: //f1和f2可行 //属于权限的缩小 void f1() { f2(); } void f2() const { } //f3和f4不可行 //属于权限的放大 //因为f4只读但f3变成可读可写 void f3() { } void f4() const { f3(); } private: int _year; int _month; int _day; };8.取地址及const取地址操作符重载1.直接用#includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } //void Print(const Date* this) void Print() const { cout _year - _month - _day; } private: int _year; int _month; int _day; }; int main() { Date d1; Date d2; cout d1 endl; cout d2 endl; return 0; }2.重载1d3没有调用取地址操作符重载#includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } //重载 Date* operator() { cout Date* operator() endl; return this; } void Print() const { cout _year - _month - _day; } private: int _year; int _month; int _day; }; int main() { Date d1; Date d2; const Date d3; cout d1 endl; cout d2 endl; cout d3 endl; return 0; }2d3调用取地址操作符重载#includeiostream using namespace std; class Date { public: Date(int year 0, int month 1, int day 1) { _year year; _month month; _day day; } Date* operator() { cout Date* operator() endl; return this; } const Date* operator() const { cout Date* operator() const endl; return this; } void Print() { cout _year - _month - _day; } private: int _year; int _month; int _day; }; int main() { Date d1; Date d2; const Date d3; cout d1 endl; cout d2 endl; cout d3 endl; return 0; }