从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


目录
相关文章
|
20天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
30 2
|
26天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
67 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
70 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
81 4
|
16天前
|
算法 编译器 C语言
【C语言】C++ 和 C 的优缺点是什么?
C 和 C++ 是两种强大的编程语言,各有其优缺点。C 语言以其高效性、底层控制和简洁性广泛应用于系统编程和嵌入式系统。C++ 在 C 语言的基础上引入了面向对象编程、模板编程和丰富的标准库,使其适合开发大型、复杂的软件系统。 在选择使用 C 还是 C++ 时,开发者需要根据项目的需求、语言的特性以及团队的技术栈来做出决策。无论是 C 语言还是 C++,了解其优缺点和适用场景能够帮助开发者在实际开发中做出更明智的选择,从而更好地应对挑战,实现项目目标。
43 0
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
30 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
26 4
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
25 1
|
2月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
2月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)