2. 日期类完整代码
Date.h:
#pragma once #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; //if (!CheckDate()) //{ // Print(); // cout << "刚构造的日期非法" << endl; //} assert(CheckDate()); } void Print() const; // 打印 int GetMonthDay(int year, int month)// 获取某年某月的天数 { 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))) { day += 1; } return day; } bool CheckDate()// 检查日期是否合法 { if (_year >= 1 && _month > 0 && _month < 13 && _day > 0 && _day <= GetMonthDay(_year, _month)) { return true; } else { return false; } } bool operator==(const Date& d) const; bool operator>(const Date& d) const; bool operator!=(const Date& d) const; bool operator>=(const Date& d) const; bool operator<(const Date& d) const; bool operator<=(const Date& d) const; Date& operator+=(int day); Date operator+(int day) const; Date& operator-=(int day); Date operator-(int day) const; // 特殊处理,使用重载区分,后置++重载增加一个int参数跟前置构成函数重载进行区分 Date& operator++(); // 前置 Date operator++(int); // 后置 Date& operator--();// 前置 Date operator--(int);// 后置 int operator-(const Date& d) const; //日期减日期 void PrintWeekDay() const; //返回*this是星期几 private: int _year; int _month; int _day; };
Date.c:
#include "Date.h" // void Date::Print(const Date* const this) void Date::Print() const { cout << _year << "年" << _month << "月" << _day << "日" << endl; } // 任何一个类,只需要写一个> == 或者 < ==重载 剩下比较运算符重载复用即可 bool Date::operator== (const Date& d) const { return _year == d._year && _month == d._month && _day == d._day; } bool Date::operator>(const Date& d) const { if ((_year > d._year) || (_year == d._year && _month > d._month) || (_year == d._year && _month == d._month && _day > d._day)) { return true; } else { return false; } } bool Date::operator!=(const Date& d) const { return !(*this == d); } bool Date::operator>=(const Date& d) const { return (*this > d) || (*this == d); } bool Date::operator<(const Date& d) const { return !(*this >= d); } bool Date::operator<=(const Date& d) const { return !(*this > d); } Date& 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; } Date Date::operator+(int day) const { Date ret = *this; ret += day; return ret;// 出了作用域ret对象就不在了,所以不能用引用返回 } Date& Date::operator-=(int day) { if (day < 0) { return *this += -day; } _day -= day; while (_day <= 0) { _month--; if (_month == 0) { _year--; _month = 12; } _day += GetMonthDay(_year, _month); } return *this; } Date Date::operator-(int day) const { Date ret = *this; ret -= day;// ret.operator-=(day); return ret;// 和 + 一样,出了作用域ret对象就不在了,所以不能用引用返回 } Date& Date::operator++() // 前置 { return *this += 1; } Date Date::operator++(int) // 后置 { Date ret = *this; *this += 1; return ret; } Date& Date::operator--() // 前置 { return *this -= 1; } Date Date::operator--(int) // 后置 { Date ret = *this; *this -= 1; return ret; } int Date::operator-(const Date& d) const { int ret = 0; int flag = -1; Date min = *this;//默认第一个小,返回的时候乘上 -1 Date max = d; if (*this > d)//默认错误,把小和大重置,返回时乘上 1 { flag = 1; min = d; max = *this; } while (min != max) { ++min; ++ret; } return ret * flag; } void Date::PrintWeekDay() const //打印*this是星期几 { const char* Week[] = { "星期一","星期二" ,"星期三" , "星期四" ,"星期五" , "星期六" , "星期天" }; Date flag(1900, 1, 1); //1900年1月1日是星期一,自己减自己为0,对应下标0 cout << Week[(*this - flag) % 7] << endl; }
Test.c:
#include "Date.h" void TestDate1() { Date d1; d1.Print(); Date d2(2023, 5, 4); d2.Print(); Date d3(2026, 5, 1); Date d4(2020, 5, 20); cout << (d2 > d3) << endl; cout << (d2 == d3) << endl; cout << (d2 != d3) << endl; cout << (d2 >= d4) << endl; cout << (d2 < d4) << endl; cout << (d2 <= d4) << endl; } void TestDate2() { Date d1(2023, 5, 4); Date d2 = d1 + 14; d2.Print();//我们还可以这样写: (d1 + 40).Print();// 跨月 (d1 + 400).Print();// 跨年 (d1 + 4000).Print(); // 跨闰年 (d1 + 40000).Print(); } void TestDate3() { Date d1(2023, 5, 4); Date d2 = d1 - -4; d2.Print();//我们还可以这样写: (d1 - -40).Print();// 跨月 (d1 - -400).Print();// 跨年 (d1 - -4000).Print(); // 跨闰年 (d1 - 4000).Print(); // 跨闰年 (d1 - 40000).Print(); } void TestDate4() { Date d1(2023, 5, 4); Date d2 = ++d1; d1.Print(); d2.Print(); Date d3(2023, 5, 31); Date d4 = d3++; d3.Print(); d4.Print(); } void TestDate5() { Date d1(2023, 5, 4); Date d2 = --d1; d1.Print(); d2.Print(); Date d3(2023, 5, 1); Date d4 = d3--; d3.Print(); d4.Print(); } void TestDate6() { Date d1(2023, 5, 5); Date d2(2023, 6, 7); d1.Print(); d2.Print(); cout << (d1 - d2) << endl << endl; Date d3(2023, 5, 5); Date d4(2000, 1, 1); d3.Print(); d4.Print(); cout << (d3 - d4) << endl << endl; Date d5(2100, 1, 1); Date d6(2000, 1, 1); d5.Print(); d6.Print(); cout << (d5 - d6) << endl; } void TestDate7() { Date d1(2023, 5, 5); Date d2(2023, 6, 7); d1.Print(); d1.PrintWeekDay(); d2.Print(); d2.PrintWeekDay(); Date d3(1900, 1, 7); Date d4(2050, 6, 7); d3.Print(); d3.PrintWeekDay(); d4.Print(); d4.PrintWeekDay(); } int main() { //TestDate1(); //TestDate2(); //TestDate3(); //TestDate4(); //TestDate5(); //TestDate6(); TestDate7(); return 0; }
3. 笔试选择题
再贴下知识点复习链接:
从C语言到C++⑤(第二章_类和对象_中篇)(6个默认成员函数+运算符重载+const成员)_GR C的博客-CSDN博客
3.1 下列关于构造函数的描述正确的是( )
A.构造函数可以声明返回类型
B.构造函数不可以用private修饰
C.构造函数必须与类名相同
D.构造函数不能带参数
3.2 假定MyClass为一个类,则该类的拷贝构造函数的声明语句是( )
A.MyClass(MyClass x)
B.MyClass &(MyClass x)
C.MyClass(MyClass &x)
D.MyClass(MyClass *x)
3.3 在函数F中,本地变量a和b的构造函数(constructor)和析构函数(destructor)的调用顺序是: ( )
Class A; Class B; void F() { A a; B b; }
A.b构造 a构造 a析构 b析构
B.a构造 a析构 b构造 b析构
C.b构造 a构造 b析构 a析构
D.a构造 b构造 b析构 a析构
3.4 设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?( )
C c; int main() { A a; B b; static D d; return 0; }
B.B A D C
C.C D B A
D.A B D C
3.5 拷贝构造函数的特点是( )
A.该函数名同类名,也是一种构造函数,该函数返回自身引用
B.该函数只有一个参数,是对某个对象的引用
C.每个类都必须有一个拷贝初始化构造函数,如果类中没有说明拷贝构造函数,则编译器系统会自动生成一个缺省拷贝构造函数,作为该类的保护成员
D.拷贝初始化构造函数的作用是将一个已知对象的数据成员值拷贝给正在创建的另一个同类的对象
3.6 已知表达式++a中的"++"是作为成员函数重载的运算符,则与++a等效的运算符函数调用形式为( )
A.a.operator++()
B.a.operator++(0)
C.a.operator++(int)
D.operator++(a,0)
3.7 在重载一个运算符为成员函数时,其参数表中没有任何参数,这说明该运算符是 ( )
A.无操作数的运算符
B.二元运算符
C.前缀一元运算符
D.后缀一元运算符
3.8 哪个操作符不能被重载 ( )
A.*
B.()
C.. (点)
D.[]
E.->
3.9 若要对data类中重载的加法运算符成员函数进行声明,下列选项中正确的是( )
A.Data operator+(Data);
B.Data operator(Data);
C.operator+(Data,Data);
D.Data+(Data);
3.10 假设 AA 是一个类, AA* abc () const 是该类的一个成员函数的原型。若该函数返回 this 值,当用 x.abc ()调用该成员函数后, x 的值是( )
A.可能被改变
B.已经被改变
C. 受到函数调用的影响
D.不变
3.11 下列关于赋值运算符“=”重载的叙述中,正确的是( )
A.赋值运算符只能作为类的成员函数重载
B.默认的赋值运算符实现了“深层复制”功能
C.重载的赋值运算符函数有两个本类对象作为形参
D.如果己经定义了复制拷贝构造函数,就不能重载赋值运算符
答案解析:
3.1 C
A.构造函数不能有返回值,包括void类型也不行
B.构造函数可以是私有的,只是这样之后就不能直接实例化对象
C.这是必须的
D.构造函数不光可以带参数,还可以有多个构造函数构成重载
3.2 C
A.参数必须是引用,否则造成无限递归
B.语法错误
C.正确
D.这种写法只是普通的构造函数,不能成为拷贝构造函数
3.3 D
A.构造顺序是按照语句的顺序进行构造,析构是按照构造的相反顺序进行析构,因此先构造b错误
B.a析构的时机不对,对象析构要在生存作用域结束的时候才进行析构,因此先析构a错误
C.b的构造时机错误,先构造a
D.正确,构造顺序是按照语句的顺序进行构造,析构是按照构造的相反顺序进行析构
3.4 B
分析:1、类的析构函数调用一般按照构造函数调用的相反顺序进行调用,但是要注意static对象的存在, 因为static改变了对象的生存作用域,需要等待程序结束时才会析构释放对象
2、全局对象先于局部对象进行构造
3、局部对象按照出现的顺序进行构造,无论是否为static
4、所以构造的顺序为 c a b d
5、析构的顺序按照构造的相反顺序析构,只需注意static改变对象的生存作用域之后,会放在局部 对象之后进行析构
6、因此析构顺序为B A D C
3.5 D
A.拷贝构造函数也是一构造函数,因此不能有返回值
B.该函数参数是自身类型的对象的引用
C.自动生成的缺省拷贝构造函数,作为该类的公有成员,否则无法进行默认的拷贝构造
D.用对象初始化对象这是拷贝构造函数的使命,故正确
3.6 A
A.正确
B.operator++()传递了整形参数,故为后置++,错误
C.调用函数传递类型,导致语法错误
D.参数过多,语法错误
3.7 C
A.重载为成员函数时,其函数的参数个数与真实的函数参数个数会减少1个,减少的则 通过this指针进行传递,所以无参 则说明有一个参数,故错误
B.无参成员函数相当于有一个参数的全局函数,不能是二元运算符
C.正确
D.区分前缀后缀时,后缀运算需要加一个int参数
3.8 C
A.可以,例如重载对象取值,典型有以后学到的智能指针
B.可以,例如以后学到的仿函数就是通过重载()实现的
C.不能,不能被重载的运算符只有5个, 点号. 三目运算?: 作用域访 问符:: 运算符sizeof 以及.*
D.可以,例如重载对象的指向,典型有以后学到的智能指针
3.9 A
A.正确
B.语法错误,缺少运算符+
C.成员函数参数过多
D.没有运算符重载关键字operator
3.10 D
A.此成员函数被定义为const常方法,代表在函数内部不能修改任何当前对象的数据成员,因此x不可能改变
B.错误,不能被改变
C.x的值在函数内部不受任何影响
D.正确
3.11 A
A. 赋值运算符在类中不显式实现时,编译器会生成一份默认的,此时用户在类外再将赋值运算符重载为全局的,就和编译器生成的默认赋值运算符冲突了,故赋值运算符只能重载成成员函数
B.默认的赋值运算符是按成员成员,属于浅赋值
C.参数只有一个,另一个通过this指针传递
D.两个函数的调用场景不同,相互没有影响
本篇完。