从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题(下)

简介: 从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题

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.两个函数的调用场景不同,相互没有影响

本篇完。



510d299afd0b4f95989cd8066a224387.png


目录
相关文章
|
5月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
119 0
|
5月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
203 0
|
7月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
260 12
|
8月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
8月前
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
|
2月前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
829 0
|
4月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
271 15
|
10月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
436 23
|
9月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
284 1
一文彻底搞清楚C语言的函数
|
10月前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
354 15
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】