登封网站建设公司设备网站模板
news/
2025/9/27 13:48:51/
文章来源:
登封网站建设公司,设备网站模板,函数自定义wordpress,郴州市建设局网站节能科一、类的6个默认成员函数 如果一个类中什么成员都没有#xff0c;简称为空类。但是空类中并不是真的什么都没有#xff0c;任何类在什么都不写的时候#xff0c;编译器会自动生成以下 6 个默认成员函数。 默认成员函数#xff1a;用户没有显式实现#xff0c;编译器会生成…一、类的6个默认成员函数 如果一个类中什么成员都没有简称为空类。但是空类中并不是真的什么都没有任何类在什么都不写的时候编译器会自动生成以下 6 个默认成员函数。 默认成员函数用户没有显式实现编译器会生成的成员函数称为默认成员函数。 class Date{// 空类
}; 二、构造函数
1、概念
class Date
{
public:void Init(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout _year - _month - _day endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Init(2023, 1, 26);d1.Print();Date d2;d2.Init(2023, 8, 9);d2.Print();return 0;
} 对于 Date 类可以通过 Init 公有方法给对象设置日期但如果每次创建对象时都调用该方法设置信息未免有点麻烦或者有时候忘记初始化那能否在对象创建时就将信息设置进去呢 构造函数是一个特殊的成员函数 名字与类名相同 创建类类型对象时由编译器自动调用以保证每个数据成员都有 一个合适的初始值并且在对象整个生命周期内 只调用一次 。 2、特性 构造函数是特殊的成员函数不能以普通函数的定义和调用规则去理解需要注意的是构造函数虽然名称叫构造但是构造函数的主要任务并不是开空间创建对象而是 初始化对象 。 特征 函数名与类名相同。 无返回值也不用写 void。对象实例化时编译器自动调用对应的构造函数。 构造函数可以重载。 class Date
{
public:// 1、无参构造函数Date(){_year 1;_month 1;_day 1;}// 写法相同/*Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}*/// 2、带参构造函数Date(int year, int month, int day){_year year;_month month;_day day;}
private:int _year;int _month;int _day;
};void TestDate()
{Date d1; // 调用无参构造函数Date d2(2023, 9, 12); // 调用带参的构造函数Date d3(); // 声明了d3函数该函数无参返回一个日期类型的对象// warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义呢?)
} 注意如果通过无参构造函数创建对象时对象后面不用跟括号否则就成了函数声明。 5、如果类中没有显式定义构造函数则 C 编译器会自动生成一个无参的默认构造函数一旦用户显式定义编译器将不再生成。 class Date
{
public:
/*// 如果用户显式定义了构造函数编译器将不再生成Date(int year, int month, int day){_year year;_month month;_day day;}
*/void Print(){cout _year - _month - _day endl;}private:int _year;int _month;int _day;
};int main()
{ Date d1;// 无参构造函数放开后报错error C2512: “Date”: 没有合适的默认构造函数可用return 0;
} 将 Date 类中构造函数屏蔽后代码可以通过编译因为编译器生成了一个无参的默认构造函数。 将 Date 类中构造函数放开代码编译失败因为一旦显式定义任何构造函数编译器将不再生成。 6、关于编译器生成的默认成员函数在不实现构造函数的情况下编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用d 对象调用了编译器生成的默认构造函数但是 d 对象 _year / _month / _day依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用 C 把类型分成内置类型基本类型和自定义类型。 内置类型就是语言提供的数据类型如int / double / char / 指针等。自定义类型就是我们使用 class / struct / union 等自己定义的类型。 从下面的代码中可以发现编译器生成默认的构造函数会对自定类型成员 _t 调用的它的默认成员函数。也就是说默认生成的构造函数对内置类型成员不作处理对自定义类型成员会去调用它的默认构造函数。这个设计是 C 早期设计的一个缺陷本来应该内置类型也一并处理。 对于类中的内置类型成员 — 不处理为随机值除非声明时给了缺省值 - C11对于类中的自定义类型成员 — 自动调用它的默认构造函数不要参数就可以调用的比如 无参构造函数 或 全缺省构造函数 class Time
{
public:Time(){cout Time() endl;_hour 0;_minute 0;_second 0;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};int main()
{Date d;return 0;
} 注意 C11 中针对内置类型成员不初始化的缺陷又打了补丁即内置类型成员变量在 类中声明时可以给默认值注意这里不是初始化是声明给缺省值。 class Time
{
public:Time(){cout Time() endl;_hour 0;_minute 0;_second 0;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型(内置类型)// 给缺省值int _year 1; // 声明int _month 1;int _day 1;// 自定义类型Time _t;
};int main()
{Date d;return 0;
} 7、无参的构造函数和全缺省的构造函数都称为默认构造函数并且默认构造函数只能有一个。 注意 默认构造函数不传参数就可以调用的 无参构造函数。全缺省构造函数。没写编译器默认生成的构造函数。 class Date
{
public:// 1、无参构造函数Date(){_year 1;_month 1;_day 1;}// 2、带参构造函数Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}
private:int _year;int _month;int _day;
};int main()
{Date d1; // 无法通过编译 - 调用存在二义性return 0;
} 构造函数的特点不用传参数就可以调用。 一般的类都不会让编译器默认生成构造函数大部分都会自己去写。显示写一个全缺省很好用。特殊情况才会默认生成构造函数例如 Myqueue 这样的类。每个类最好都要提供默认构造函数。 三、析构函数
1、概念 通过前面构造函数的学习我们知道一个对象是怎么来的那一个对象又是怎么没的呢 析构函数与构造函数功能相反析构函数不是完成对对象本身的销毁局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数完成对象中资源的清理工作。 构造函数是为了替代 Init析构函数是为了替代 Destroy。 2、特性 析构函数是特殊的成员函数其特征如下 析构函数名是在类名前加上字符 ~。 无参数无返回值类型。 一个类只能有一个析构函数。若未显式定义系统会自动生成默认的析构函数。注意析构函数不能重载。对象生命周期结束时C 编译系统系统自动调用析构函数。后定义的先析构跟数据结构中的栈性质相同。 typedef int DataType;
class Stack
{
public:Stack(size_t capacity 4){_array (DataType*)malloc(sizeof(DataType) * capacity);if (NULL _array){perror(malloc申请空间失败!!!);return;}_capacity capacity;_size 0;}void Push(DataType data){// CheckCapacity();_array[_size] data;_size;}// 其他方法...~Stack(){if (_array){free(_array);_array NULL;_capacity 0;_size 0;}}
private:DataType* _array;int _capacity;int _size;
};void TestStack()
{Stack s;s.Push(1);s.Push(2);
} 6、关于编译器自动生成的析构函数从下面的程序可以看到编译器生成的默认析构函数对自定义类型成员调用它的析构函数。 对于类中的内置类型成员 — 不处理。 对于类中的自定义类型成员 — 调用它的析构函数完成清理工作。 class Time
{
public:~Time(){cout ~Time() endl;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型(内置类型)int _year 1970;int _month 1;int _day 1;// 自定义类型Time _t;
};int main()
{Date d;return 0;
}
// 程序运行结束后输出~Time() 在 main 方法中根本没有直接创建 Time 类的对象为什么最后会调用Time类的析构函数 因为 main 方法中创建了 Date 对象 d而 d 中包含 4 个成员变量其中 _year, _month, _day三个是内置类型成员销毁时不需要资源清理最后系统直接将其内存回收即可而 _t 是 Time 类对象所以在 d 销毁时要将其内部包含的 Time 类的 _t 对象销毁所以要调用 Time 类的析构函数。但是 main 函数中不能直接调用 Time 类的析构函数实际要释放的是 Date 类对象所以编译器会调用 Date 类的析构函数而 Date 没有显式提供则编译器会给 Date 类生成一个默认的析构函数目的是在其内部调用 Time 类的析构函数即当 Date 对象销毁时要保证其内部每个自定义对象都可以正确销毁。main 函数中并没有直接调用 Time 类析构函数而是显式调用编译器为 Date 类生成的默认析构函数。 注意创建哪个类的对象则调用该类的析构函数销毁那个类的对象则调用该类的析构函数。 7、如果类中没有申请资源时析构函数可以不写直接使用编译器生成的默认析构函数比如 Date 类有资源申请时一定要写否则会造成资源泄漏比如 Stack 类。 默认生成析构函数的特点跟构造函数类似 一些内置类型的类不作处理。比如 Date这样的类没有资源需要清理比如 MyQueue 也可以不写默认生成的就可以。自定义类型成员回去调用它的析构函数比如Stack、Queue... 四、拷贝构造函数
1、概念 在创建对象时可否创建一个与已存在对象一某一样的新对象呢 拷贝构造函数 只有单个形参该形参是对本类类型对象的引用一般常用 const 修饰在用已存在的类类型对象创建新对象时由编译器自动调用。 2、特征 拷贝构造函数也是特殊的成员函数其特征如下 拷贝构造函数是构造函数的一个重载形式。拷贝构造函数的参数只有一个且必须是类类型对象的引用使用传值方式编译器会直接报错因为会引发无穷递归调用。 class Date
{
public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}Date(const Date d) // 正确写法//Date(const Date d) // 错误写法编译报错会引发无穷递归{_year d._year;_month d._month;_day d._day;}private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(d1);return 0;
} 使用引用作为拷贝构造函数的参数可以有效避免无穷递归调用的问题。因为引用在初始化时不会调用拷贝构造函数而是直接将引用绑定到已经存在的对象上。 tips建议拷贝构造函数的参数类型前加上 const防止其值误被修改。 3、若未显示定义编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。 浅拷贝 一个对象修改会影响另一个对象。会析构两次程序崩溃。 解决方法自己实现深拷贝。 如果没有显式定义编译器自动生成的拷贝构造函数它会做哪些事情呢 对于类中的内置类型成员 — 值拷贝。对于类中的自定义类型成员 — 自动调用它的拷贝构造函数来完成拷贝初始化。 class Time
{
public:Time(){_hour 1;_minute 1;_second 1}Time(const Time t){_hour t._hour;_minute t._minute;_second t._second;cout Time::Time(const Time) endl;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型(内置类型)int _year 1;int _month 1;int _day 1;// 自定义类型Time _t;
};int main()
{Date d1;Date d2(d1);return 0;
} 用已经存在的 d1 拷贝构造 d2此处会调用 Date 类的拷贝构造函数。但 Date 类并没有显式定义拷贝构造函数则编译器会给 Date 类生成一个默认的拷贝构造函数。 注意 在编译器生成的默认拷贝构造函数中内置类型是 按照字节方式直接拷贝 的而自定 义类型是 调用其拷贝构造函数 完成拷贝的。 4、编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了还需要自己显式实现吗当然像日期类这样的类是没必要的。那么下面的类呢 像日期类这样的类是没必要的。但有些需要深拷贝的类其内部往往是很复杂的是需要用户显式定义拷贝构造函数来完成深拷贝的。 // 下面这个程序会崩溃掉因为这里就需要深拷贝去解决。
typedef int DataType;class Stack
{
public:Stack(size_t capacity 10){_array (DataType*)malloc(capacity * sizeof(DataType));if (nullptr _array){perror(malloc申请空间失败);return;}size 0;_capacity capacity;}void Push(const DataType data){// CheckCapacity();_array[_size] data;_size;}~Stack(){if (_array){free(_array);_array nullptr;_capacity 0;_size 0;}}private:DataType *_array;size_t _size;size_t _capacity;
};int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2(s1); // 会造成指向的同一块区域的内容被两次释放return 0;
} 注意 类中如果没有涉及资源申请时拷贝构造函数是否写都可以一旦涉及到资源申请 时则拷贝构造函数是一定要写的否则就是浅拷贝。 如图指向了同一块空间。 那么会引发什么问题呢会导致 _str 指向的空间被释放两次引发程序崩溃。 5、拷贝构造函数典型调用场景 使用已存在对象创建新对象。函数参数类型为类类型对象。函数返回值类型为类类型对象。 class Date
{
public:Date(int year, int minute, int day){cout Date(int, int, int): this endl;}Date(const Date d){cout Date(const Date d): this endl;}~Date(){cout ~Date(): this endl;}
private:int _year;int _month;int _day;
};Date Test(Date d)
{Date temp(d);return temp;
}int main()
{Date d1(2023 ,9, 13);Test(d1);return 0;
} 为了提高程序效率一般对象传参时尽量使用引用类型返回时根据实际场景能用引用 尽量使用引用。 一些类需要显示写拷贝和赋值比如Stack、Queue... 一些类不需要显示写拷贝和赋值。比如 Date 这样的类默认生成就会完成值拷贝 / 浅拷贝比如 MyQueue 这样的类默认生成就会调用它的自定义类型成员 Stack 的拷贝和赋值。 五、赋值运算符重载
1、运算符重载 C 为了增强代码的可读性引入了 运算符重载 运算符重载是具有特殊函数名的函数也具有其返回值类型函数名字以及参数列表其返回值类型与参数列表与普通的函数类似。 函数名字关键字 operator 后面接需要重载的运算符符号。 函数原型返回值类型 operator 操作符参数列表。 内置类型可以直接使用运算符运算编译器知道要如何运算。 自定义类型无法直接使用运算符编译器不知道要如何运算。 注意 不能通过连接其他符号来创建新的操作符比如 operator。重载操作符必须有一个类类型参数。用于内置类型的运算符其含义不能改变例如内置的整型 不能改变其含义。作为类成员函数重载时其形参看起来比操作数数目少 1因为成员函数的第一个参数为隐藏的 this。.* :: sizeof ?: . 注意以上 5 个运算符不能重载。 // 全局的operator
class Date
{
public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}//private:int _year;int _month;int _day;
};
// 这里会发现运算符重载成全局的就需要成员变量是公有的那么要如何保证封装性呢
// 这里其实可以用友元解决或者干脆重载成成员函数。bool operator(const Date d1, const Date d2)
{return d1._year d2._year d1._month d2._month d1._day d2._day;
}void Test ()
{Date d1(2023, 9, 13);Date d2(2023, 9, 14);cout (d1 d2) endl;
} 运算符重载一般有两种方式 重载成类的成员函数形参数目看起来比该运算符需要的参数少一个因为成员函数有隐含的 this 指针且函数的第一个形参就是 this 指针。重载成类的友元函数必须有一个参数要是类的对象一般不这样做而是重载成成员函数。 下面这种写法更好
class Date
{
public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}// bool operator(Date* this, const Date d2)bool operator(const Date d2) // 注意左操作数是隐藏的this指向调用函数的对象{return _year d2._year _month d2._month _day d2._day;}private:int _year;int _month;int _day;
};void Test ()
{Date d1(2023, 9, 13);Date d2(2023, 9, 14);cout d1.operator(d2) endl; // d1.operator(d1, d2);
} 2、赋值运算符重载
1赋值运算符重载格式 参数类型const T传递引用可以提高传参效率。返回值类型T返回引用可以提高返回的效率有返回值目的是为了支持连续赋值。检测是否自己给自己赋值。返回 *this 要复合连续赋值的含义。 class Date
{
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;return *this; // 支持连续赋值}Date operator(const Date d) // 传引用返回{if(this ! d) // 检测是否自己给自己赋值{_year d._year;_month d._month;_day d._day;} return *this;
}
private:int _year ;int _month ;int _day ;
};void TestDate()
{Date d1(2023, 9, 13);Date d2(d1);Date d3(2023, 11, 28);d2 d1 d3;
} 2赋值运算符只能重载成类的成员函数不能重载成全局函数
class Date
{
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}int _year;int _month;int _day;
};// 赋值运算符重载成全局函数注意重载成全局函数时没有this指针了需要给两个参数
Date operator(Date left, const Date right)
{if (left ! right){left._year right._year;left._month right._month;left._day right._day;}return left;
} 编译失败error C2801: “operator ”必须是非静态成员 原因 赋值运算符如果不显式实现编译器会生成一个默认的。此时用户再在类外自己实现 一个全局的赋值运算符重载就和编译器在类中生成的默认赋值运算符重载冲突了故赋值 运算符重载只能是 类的成员函数 。 3用户没有显式实现时编译器会生成一个默认赋值运算符重载以值的方式逐字节拷贝 注意 内置类型成员变量是直接赋值的而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。 class Time
{
public:Time(){_hour 1;_minute 1;_second 1;}Time operator(const Time t){if (this ! t){_hour t._hour;_minute t._minute;_second t._second;}return *this;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型内置类型int _year 1;int _month 1;int _day 1;// 自定义类型Time _t;
};int main()
{Date d1;Date d2;d1 d2;return 0;
} 既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了还需要自己实现吗当然像日期类这样的类是没必要的。那么下面的类呢 像日期类这样的类是没必要的。但有些需要深拷贝的类其内部往往是很复杂的是需要用户显式定义赋值运算符重载函数来完成深拷贝的。 // 下面这个程序会崩溃这里需要用深拷贝去解决。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity 10){_array (DataType*)malloc(capacity * sizeof(DataType));if (nullptr _array){perror(malloc申请空间失败);return;}_size 0;_capacity capacity;}void Push(const DataType data){// CheckCapacity();_array[_size] data;_size;}~Stack(){if (_array){free(_array);_array nullptr;_capacity 0;_size 0;}}private:DataType *_array;size_t _size;size_t _capacity;
};int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 s1;return 0;
} 注意如果类中未涉及到资源管理赋值运算符是否实现都可以一旦涉及到资源管理则必须要实现。 3、前置和后置重载
class Date
{
public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}Date operator() // 前置返回1之后的结果。{_day 1;return *this; // this 指向的对象函数结束后不会销毁故以引用的方式返回提高效率。}Date operator(int) // 后置是先使用后1因此需要返回1之前的旧值{Date temp(*this); // 在实现时需要先将this保存一份然后给this1_day 1;return temp; // 因为temp是临时对象因此只能以值的方式返回不能返回引用}private:int _year;int _month;int _day;
};int main()
{Date d;Date d1(2023, 9, 13);d d1; // d: 2023,9,13 d1:2023,9,14d d1; // d: 2023,9,14 d1:2023,9,14return 0;
} 前置和后置都是一元运算符为了让前置与后置形成能正确重载C 规定后置重载时多增加一个 int 类型的参数但调用函数时该参数不用传递编译器自动传递。前置返回的是引用后置返回的是值。 六、日期类的实现
1、Date.h
// Date.h
#pragma once#includeiostream
#includeassert
#includestdbool.h
using namespace std;class Date
{
public:// 获取某年某月的天数int GetMonthDay(int year, int month){assert(month 1 month 12);// 每月的天数这个函数会被频繁调用每次进来都要重新定义数组所以将其定义为静态的// 默认是平年static int days[13] {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};int day days[month];if (month 2 ((year % 4 0 year % 100 ! 0) || (year%400 0)))// 把month 2写在前面可以直接筛选出更少的内容{day 1; // 闰年的二月是29天}return day;}Date(int year 1, int month 1, int day 1) // 全缺省的构造函数{_year year;_month month;_day day;//判断日期是否合法if (_year 0 || _month 0 || _month 13 || _day 0 || _day GetMonthDay(_year, _month)){cout _year / _month / _day -;cout 非法日期 endl;}}// 打印日期void Print(){cout _year / _month / _day endl;}// 拷贝构造、赋值运算符、析构函数用编译器自动生成的就可以了因为Date类是浅拷贝// 日期 天数 -- d1 100Date operator(int day);// 日期 天数 -- d1 100Date operator(int day);// 日期 - 天数 -- d1 - 100Date operator-(int day);// 日期 - 天数 -- d1 - 100Date operator-(int day);// 前置Date operator(); // 编译器会解释为Date operator(Date* const this);// 后置Date operator(int); // 编译器会解释为Date operator(Date* const this, int);// 前置--Date operator--();// 后置--Date operator--(int);// 运算符重载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;elsereturn false;}// 运算符重载bool operator(const Date d){return _year d._year _month d._month _day d._day;}// 这里我们只需要把和运算符重载了下面的运算符都可以复用其代码了// 运算符重载bool operator(const Date d){return *this d || *this d; // 复用operator、operator}// 运算符重载bool operator(const Date d){return !(*this d); // 复用operator再取反}// 运算符重载bool operator(const Date d){return !(*this d); // 复用operator再取反}// !运算符重载bool operator!(const Date d){return !(*this d); // 复用operator再取反}// 日期 - 日期返回相差天数 -- d1 - d2int operator-(const Date d);private:int _year;int _month;int _day;
}; 2、Date.cpp
1日期 天数返回累加天数后的日期 比如d1 100 注意d1本身要被更改天数累加到 d1 上面去。 Date Date::operator(int day)
{if (day 0) // 如果day是负数就向前计算相当于 -{return *this - -day; // 调用-运算符重载函数}_day day; // 累加天数// 日期不合法需要进位while (_day GetDays(_year, _month)) // 表示当前月的天数已经过完了{_day - GetDays(_year, _month); // 减去当前月的天数_month; // 月进位if (_month 13) // 判断当前月份是否合法{_year; // 年进位_month 1; // 更新为1月}}return *this;/* 写法二复用运算符重载函数的代码*this *this day; // d1等价于*this对d1进行天数操作再赋值给d1return *this; // 返回d1*/
}2日期 天数返回累加天数后的日期 比如 d1 100 注意d1本身不能被更改天数累加到一个临时对象上面去。 // 写法一
Date Date::operator(int day)
{Date tmp(*this); // 拷贝构造一份临时对象防止调用本函数的对象被更改tmp._day day; // 累加天数while (tmp._day GetDays(tmp._year, tmp._month)) // 表示当前月的天数已经过完了{tmp._day - GetDays(tmp._year, tmp._month); // 减去当前月的天数tmp._month; // 月进位if (tmp._month 13) // 判断当前月份是否合法{tmp._year; // 年进位tmp._month 1; // 更新为1月}}return tmp; // 返回临时对象
}// 写法二
Date Date::operator(int day)
{/* 复用 运算符重载函数的代码 */Date tmp(*this); // 拷贝构造一份临时对象tmp day; // 对临时对象进行 天数操作return tmp; // 返回临时对象
}3日期 - 天数返回累减天数后的日期 比如d1 - 100 Date Date::operator-(int day)
{if (day 0) // 如果day小于0就往后计算相当于 {return *this -day; // 调用运算符重载函数}_day - day; // 累减天数while (_day 0) // 说明天数不够减了需要向上一个月去借{_month--; // 月份-1if (_month 0){_year--;_month 12;}_day GetDays(_year, _month); // 借上一个月的天数}return *this;
} 4日期 - 天数返回累减天数后的日期 比如d1 - 100 Date Date::operator-(int day)
{// 复用 - 运算符重载函数的代码Date tmp(*this); // 拷贝构造一份临时对象tmp - day; // 对临时对象进行 - 天数操作return tmp; // 返回临时对象
} 5前置 和 后置 注意按正常的运算符重载规则无法区分 前置 和 后置为了区分这里做了一个特殊处理给 后置 增加了一个 int 参数这个参数仅仅是为了区分使 前置 和 后置 构成重载。 // 前置
// d1
Date Date::operator()
{// 复用 运算符重载函数的代码*this 1;return *this;
}// 后置
// d1
Date Date::operator(int)
{Date tmp(*this); // 保存当前对象自减前的值*this 1; // 复用 运算符重载函数的代码return tmp; // 返回当前对象自减前的值
} 6前置-- 和 后置–
// 前置--
// --d1
Date Date::operator--()
{// 复用 - 运算符重载函数的代码 *this - 1;return *this;
}// 后置--
// d1--
Date Date::operator--(int)
{Date tmp(*this); // 保存当前对象自减前的值*this - 1; // 复用 - 运算符重载函数的代码return tmp; // 返回当前对象自减前的值
} 7日期 - 日期返回相差的天数有正负之分 比如d1 - d2 思路让小的日期不断往后直到等于大的日期统计加了多少次就相差多少天。 大的日期 - 小的日期 正的天数小的日期 - 大的日期 负的天数 int Date::operator-(const Date d)
{// 判断出大的日期和小的日期Date max *this;Date min d;int flag 1; // 加一个flag变量来控制天数的正负if (max min){max d;min *this;flag -1;}// 让小的日期累加天数加了多少次说明就相差了多少天int count 0;while (min ! max){min;count;}return flag * count;
} 【总结】
// Date.cpp
#include Date.h// 日期 天数
Date Date::operator(int day)
{if (day 0) // 如果day是负数就向前计算相当于 -{return *this - -day; // 调用-运算符重载函数}_day day; // 累加天数// 日期不合法需要进位while (_day GetDays(_year, _month)) // 表示当前月的天数已经过完了{_day - GetDays(_year, _month); // 减去当前月的天数_month; // 月进位if (_month 13) // 判断当前月份是否合法{_year; // 年进位_month 1; // 更新为1月}}return *this;
}// 日期 天数
Date Date::operator(int day)
{Date tmp(*this); // 拷贝构造一份临时对象防止调用本函数的对象被更改tmp._day day; // 累加天数while (tmp._day GetDays(tmp._year, tmp._month)) // 表示当前月的天数已经过完了{tmp._day - GetDays(tmp._year, tmp._month); // 减去当前月的天数tmp._month; // 月进位if (tmp._month 13) // 判断当前月份是否合法{tmp._year; // 年进位tmp._month 1; // 更新为1月}}return tmp; // 返回临时对象
}// 日期 - 天数
Date Date::operator-(int day)
{if (day 0) // 如果day小于0就往后计算相当于 {return *this -day; // 调用运算符重载函数}_day - day; // 累减天数while (_day 0) // 说明天数不够减了需要向上一个月去借{_month--; // 月份-1if (_month 0){_year--;_month 12;}_day GetDays(_year, _month); // 借上一个月的天数}return *this;
}// 日期 - 天数
Date Date::operator-(int day)
{// 复用 - 运算符重载函数的代码Date tmp(*this); // 拷贝构造一份临时对象tmp - day; // 对临时对象进行 - 天数操作return tmp; // 返回临时对象
}// 前置
Date Date::operator()
{// 复用 运算符重载函数的代码*this 1;return *this;
}// 后置
Date Date::operator(int)
{Date tmp(*this); // 保存当前对象自减前的值*this 1; // 复用 运算符重载函数的代码return tmp; // 返回当前对象自减前的值
}// 前置--
Date Date::operator--()
{// 复用 - 运算符重载函数的代码 *this - 1;return *this;
}// 后置--
Date Date::operator--(int)
{Date tmp(*this); // 保存当前对象自减前的值*this - 1; // 复用 - 运算符重载函数的代码return tmp; // 返回当前对象自减前的值
}// 日期 - 日期
int Date::operator-(const Date d)
{// 判断出大的日期和小的日期Date max *this;Date min d;int flag 1; // 加一个flag变量来控制天数的正负if (max min){max d;min *this;flag -1;}// 让小的日期累加天数加了多少次说明就相差了多少天int count 0;while (min ! max){min;count;}return flag * count;
} 七、const 成员 将 const 修饰的 “ 成员函数 ” 称之为 const 成员函数const 修饰类成员函数实际修饰该成员函数 隐含的 this 指针 表明在该成员函数中不能对类的任何成员进行修改。 class Date
{
public:Date(int year, int month, int day){_year year;_month month;_day day;}void Print() // void Print(Date* const this){cout Print() endl;cout year: _year endl;cout month: _month endl;cout day: _day endl endl;}void Print() const // void Print(const Date* const this){cout Print() const endl;cout year: _year endl;cout month: _month endl;cout day: _day endl endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};void Test()
{Date d1(2022,1,1);d1.Print();const Date d2(2023,1,1); // const修饰d2.Print(); // d2.Print(d2); // d2的类型是const Date*只能读不能写// 传给第一个Print会导致权限放大为可读可写
} const 修饰成员函数是有好处的这样 const 对象可以调用非 const 对象也可以调用。但并不是说所有的成员函数都要加 const 具体得看成员函数的功能如果成员函数是修改型比如operrato、Push那就不能加如果是只读型比如Print、operator那就最好加上 const。const 成员只读函数内不可以调用其它的非 const 成员可读可写函数权限放大非 const 成员可读可写函数内可以调用其它的 const 成员只读函数权限缩小。 八、取地址及const取地址操作符重载 下面这两个是默认成员函数一般不用重新定义不写编译器默认会自动生成。 class Date
{
public:Date* operator()// Date* operator(Date* const this){return this;}const Date* operator()const // const Date* operator(const Date* const this){return this;}
private:int _year; // 年int _month; // 月int _day; // 日
}; 这两个运算符一般不需要重载使用编译器生成的默认取地址的重载即可只有特殊情况才需要重载比如不想让别人获取到这个类型对象的地址。 class Date {
public:Date* operator(){return nullptr;}const Date* operator() const{return nullptr;}
private:int _year;int _month;int _day;
};
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/919541.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!