您现在的位置是:首页 >技术杂谈 >从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题网站首页技术杂谈

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

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

目录

1. 日期类详细实现

1.1 构造函数和打印函数

1.2 六个比较运算符重载

1.3 日期+=天数 和 日期+天数

1.4 日期 -= 天数 和 日期 - 天数

1.5 日期++ 和 ++日期

1.6 日期-- 和 --日期

1.7 日期 - 日期 operator- 

1.8  打印*this是星期几

2. 日期类完整代码

Date.h:

Date.c:

Test.c:

3. 笔试选择题

3.1 下列关于构造函数的描述正确的是( )

3.2 假定MyClass为一个类,则该类的拷贝构造函数的声明语句是( )

3.3 在函数F中,本地变量a和b的构造函数(constructor)和析构函数(destructor)的调用顺序是: ( )

3.4 设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?( )

3.5  拷贝构造函数的特点是( )

3.6 已知表达式++a中的"++"是作为成员函数重载的运算符,则与++a等效的运算符函数调用形式为( )

3.7 在重载一个运算符为成员函数时,其参数表中没有任何参数,这说明该运算符是 ( )

3.8 哪个操作符不能被重载 ( )

3.9 若要对data类中重载的加法运算符成员函数进行声明,下列选项中正确的是( )

3.10 假设 AA 是一个类, AA* abc () const 是该类的一个成员函数的原型。若该函数返回 this 值,当用 x.abc ()调用该成员函数后, x 的值是( )

3.11 下列关于赋值运算符“=”重载的叙述中,正确的是( )

答案解析:

本篇完。下一篇:(类和对象_下篇)


1. 日期类详细实现

上一篇我们讲了6个默认成员函数和运算符重载等知识,复习链接:

从C语言到C++⑤(第二章_类和对象_中篇)(6个默认成员函数+运算符重载+const成员)_GR C的博客-CSDN博客

为了能够更好地讲解运算符重载和融合以前知识,我们将来实现 一个"日期类" ,

日期类的拷贝构造、赋值、析构我们都可以不用写,让编译器自己生成就行了。

构造是要写的,剩下的你想写什么就写什么。

1.1 构造函数和打印函数

和以前做大练习一样,规范一点,我们声明与定义分离开来。

先放个样例,然后后面就不放了,最后面会放完整代码。

Date.h:

#include <iostream>
using namespace std;

class Date
{
public:
	// 构造会频繁调用,所以直接放在类里面(类里面的成员函数默认为内联)
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() const;  // 打印函数

private:
	int _year;
	int _month;
	int _day;
};

Date.cpp:

#include "Date.h"

// void Date::Print(const Date* const this)
void Date::Print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

Test.cpp:

#include "date.h"

void TestDate1()
{
	Date d1;
	d1.Print();

	Date d2(2023, 5, 4);
	d2.Print();
}

int main()
{
	TestDate1();

	return 0;
}

 (记录一下五一放了五天,然后五四再放半天然后还在写博客,泪目泪目)

这里的 Print 是可以加 const 的,而构造函数这里修改了成员变量,是不能加 const 的。

上一篇我们说过,加 const 是很好的,只要不改变都建议加上 const 。

我们以前讲知识点没想过:如果我们给构造函数的日期是一个非法的日期呢?

 如果有人输入了这种日期,还是能给他打印出来,这不合理,

所以我们需要设计一个函数去判断,用户输入的日期到底合不合法。

因为每个月天数不统一,所以在写判断日期合法的算法前,

我们需要先设计一个后面也用到的可以获取一年中每个月对应天数的 GetMonthDay 函数。

如何设计呢?写12个 if else?可以是可以,就是有点搓。

用 switch case 可能还好一点。和计数排序差不多,我们这里可以写个简单的哈希来解决,

我们把每个月的天数放到一个数组里,为了方便还可以把下标为 0 的位置空出来,

这里还要考虑到我们上面提到的闰年问题,闰年二月会多一天。

如果月份为 2,我们就进行判断,如果是闰年就让获取到的 day + 1 即可。

// 会频繁调用,所以直接放在类里面定义作为inline
int GetMonthDay(int year, int month)// 获取某年某月的天数
{
	//这里的数组会频繁使用,所以加上static
	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)))//先判断是否是2月
	{
		day += 1;
	}
	return day;
}

如果传入的月份是2月,就判断是否是闰年。

这里还有一个小细节就是我们是先判断传入的月份2月的,

根据 && 的特性,碰到假,后面就不会判断了,这就是一些写代码的好习惯。

然后就可以写一个检查日期是否合法的函数了:

// 会频繁调用,所以还是直接放在类里面定义作为inline
bool CheckDate()
{
	if (_year >= 1
		&& _month > 0 && _month < 13
		&& _day > 0 && _day <= GetMonthDay(_year, _month))
	{
		return true;
	}
	else
	{
		return false;
	}
}

这里一个条件放一行也是个好习惯。然后我们的构造函数就可以这样:

// 构造会频繁调用,所以直接放在类里面(类里面的成员函数默认为内联)
Date(int year = 1, int month = 1, int day = 1)
{
	_year = year;
	_month = month;
	_day = day;

	//if (!CheckDate())
	//{
	//	Print();
	//	cout << "刚构造的日期非法" << endl;
	//}

	assert(CheckDate());
}

这里使用暴力的方法,记得#include<assert.h>,然后这里就会直接报错:

然后值得一提的是类里面的成员函数和成员变量等都是被编译器看成一个整体的,

所以不用管哪个放在哪个的上面。

1.2 六个比较运算符重载

上一篇我们在讲运算符重载的时候讲了判断日期类等于的例子,

根据逻辑:任何一个类,只需要写一个> == 或者 < ==重载 剩下的比较运算符重载复用即可

日期的判断很简单,我们只需要挨个对比就可以了,结果返回布尔值,没什么好说的。

我们这里写判断等于和判断大于然后直接放出日期类比较运算符重载的部分,

判断等于:

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;
	}
}

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;

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);
}

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;
}

int main()
{
	TestDate1();

	return 0;
}

1.3 日期+=天数 和 日期+天数

日期加一个日期没什么意义,但是加天数的场景就很多了。

比如我们想让当前日期加100天:

Date d1(2023, 5, 4);
d1 += 100;
d1.Print();

Date d2(2023, 5, 4);
d3 = d2 + 100;
d2.Print();
d3.Print();

体会了上面代码复用的方便,思考一下先实现+=还是+呢?

+= 原来的值变了,+ 原来的值没有变,返回的都是+之后的值

+ 还要调用两次拷贝,所以先实现+= 好一点点

日期加天数把进位搞定就可以了,我们把天数全都加到day上,

只需要判断加完日期后天数合不合法,

看它加完后的天数有没有超出这个月的天数,如果超过了就不合法。

这个我们刚才已经实现过 GetMonthDay 了,这里就直接拿来用就行了。

如果不合法,我们就进位。天满了往月进,月再满就往年进。

因为出了作用域对象还在,我们可以使用引用返回减少拷贝:

Date& Date::operator+=(int day)//这里没考虑day是负数的情况,最后放的代码加上了
{
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;
}

+= 是改变 "本体",但是 + 并不会,所以这里可以加个 const 修饰一下。

+ 和 += 很类似,也是通过 "判断" 就可以实现的,复用一下 += :

Date Date::operator+(int day) const//不改变,用const
{
	Date ret = *this; 
	ret += day;

	return ret;// 出了作用域ret对象就不在了,所以不能用引用返回
}

因为我们 + 是复用 += 的,所以我们只测试+应该就没什么问题

 上面的日期可以用网上搜的日期计算器搜索来验证:

1.4 日期 -= 天数 和 日期 - 天数

我们刚才实现了加和加等 ,现在我们来实现一下减和减等。

加和加等是进位,那减和减等自然就是借位。

我们先把day减一下天数,此时如果日期不合法,那我们就需要进行借位操作。

和上面一样我们先实现减等:

Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

如果减完后的天数小于等于 0,就进入循环,向月 "借位" ,

因为已经借出去了,所以把 月份 -- 。还要考虑月份会不会借完的情况,

月份为 0 的时候就是没得借了,这种情况就向年借,年--,

之后加上通过 GetMonthDay 获取当月对应的天数,就是所谓的 "借",

循环继续判断,直到天数大于0的时候停止,返回 *this  。

出了作用域 *this 还在,所以我们可以使用引用返回 Date& 。

一样的,我们复用一下 -= 就可以把 - 实现出来了:

Date Date::operator-(int day) const
{
	Date ret = *this;
	ret -= day;// ret.operator-=(day);

	return ret;// 和+一样,出了作用域ret对象就不在了,所以不能用引用返回
}

 到这我们发现日期计算器是可以输入负数的,那我们的输入负数呢:

我们发现,在设计 operator-= 的时候是 <= 0 才算非法的,所以这种情况就没考虑到。

我们可以这么设计,在减天数之前对 day 进行一个特判,

因为你减负的100就相当于加正的100,就变成加了,

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;
}

我们再回去把 operator+= 处理一下:

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;
}

1.5 日期++ 和 ++日期

d1++;
++d1;

因为都是 operator++ ,为了能让编译器直到我们实现的到底是 前置++ 还是 后置++,

这里就到用到一个叫做 "参数占位" 的东西 :int ,即:

后置++ 带  " int " ,构成函数重载。

operator++(int);     // 带int,表示后置加加   d1++
operator++();        // 不带, 表示前置加加   ++d1

根据上面的经验,我们先实现返回运算符作用后的值的前置++

因为 前置++ 返回的是加加之后的值,所以我们使用引用返回:

Date& Date::operator++() // 前置
{
	return 	*this += 1;
}

这里我们直接复用 +=,加加以后的值就是 *this ,我们返回一下 *this 就行。

因为后置++返回的是加加之前的值,所以我们不用引用返回:

Date Date::operator++(int) // 后置
{
	Date ret = *this;
	*this += 1;

	return ret;
}

这里要拷贝构造两次,所以我们推荐以后自定义类型++,使用前置++ 。

对于内置类型,用前置比后置也好那么一丢丢,但现在几乎可忽略不计,

为了和自定义类型对应好看点,我们以后对于内置类型也尽量用前置好了

1.6 日期-- 和 --日期

和 operator++ 一样,operator-- 为了能够区分前置和后置,也要用 int 参数占位

 后置-- 带  " int " ,构成函数重载。

 前置-- 返回的是减减之后的值,所以我们使用引用返回:

Date& Date::operator--() // 前置
{
	return *this -= 1;
}

后置-- 返回的是减减之前的值,所以我们不用引用返回,

我们在减减之前先拷贝构造一个 "替身" ,待本体加加后,

把替身 ret 返回回去,就实现了返回减减之前的值:

Date Date::operator--(int) // 后置
{
	Date ret = *this;
	*this -= 1;

	return ret;
}

和后置++一样这里要拷贝构造两次,所以我们推荐以后自定义类型--,使用前置-- 。

1.7 日期 - 日期 operator- 

我们刚才实现的 operator是日期减天数的:

Date Date::operator-(int day) const
{
	Date ret = *this;
	ret -= day;// ret.operator-=(day);

	return ret;// 和+一样,出了作用域ret对象就不在了,所以不能用引用返回
}

日期 + 日期 没什么意义,但是日期 - 日期还是有意义的,

如果我们想要计算一下今天距离高考还有多少天呢?

那我们就需要写一个日期减日期版本的 operater 

因为有正用负,我们利用学二叉树时讲过的思想吧

之后我们用计数器的方式来实现就可以了 :

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;
}

 

1.8  打印*this是星期几

前面我们使用日期计算器得出一个日期的时候,它都会给我们那个日期是星期几,

我们也来实现一个类似的功能。

我们需要找一个标志日期,知道那一天是星期几,然后复用上面的代码就能实现这个功能,

因为十六世纪有日期被抹掉了,所以我们拿 1900年1月1号(刚好是星期一) 作为起始点,

和上面一样创建一个const数组,

两个日期相减得到的结果,%7 作为下标去访问 Week 数组里我们已经准备好的周几,

打印出来就可以了:

void Date::PrintWeekDay() const //打印*this是星期几
{
	const char* Week[] = { "星期一","星期二" ,"星期三" , "星期四" ,"星期五" , "星期六" , "星期天" };
	Date flag(1900, 1, 1); //1900年1月1日是星期一,自己减自己为0,对应下标0

	cout << Week[(*this - flag) % 7] << endl;
}

 

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;
}

A.D B A C

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

本篇完。下一篇:(类和对象_下篇)

现在没能体会到C++的方便,学完类和对象学一点STL库就能体会到了。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。