深入探究C++ 运算符重载:以日期类为例

目录

前言

一、运算符重载基础

[1.1 运算符重载原理](#1.1 运算符重载原理)

[1.2 示例代码](#1.2 示例代码)

二、赋值运算符重载

[2.1 赋值运算符重载格式](#2.1 赋值运算符重载格式)

[2.2 代码实现](#2.2 代码实现)

[2.3 注意事项](#2.3 注意事项)

三、前置++和后置++重载

[3.1 前置++重载](#3.1 前置++重载)

[3.2 后置++重载](#3.2 后置++重载)

四、日期类的完整实现

[4.1 获取某月天数](#4.1 获取某月天数)

[4.2 完整类定义](#4.2 完整类定义)

五、总结


前言

在C++ 编程中,运算符重载 是一项强大且实用的特性,它允许我们赋予运算符在自定义类上的新含义,极大地增强了代码的可读性和易用性。今天,我们就以日期类( Date )为例,深入探讨运算符重载的相关知识。

一、运算符重载基础

1.1 运算符重载原理

C++ 引入运算符重载机制,让我们能像使用内置类型运算符那样操作自定义类对象。运算符重载本质上是定义特殊函数,函数名由关键字operator和要重载的运算符组成,比如 operator+ 、 operator= 。其函数原型形如:返回值类型 operator 运算符(参数列表) 。

不过,有几点需要注意:

  • 不能随意创造新运算符,比如 operator@ 这种是不允许的。

  • 重载操作符必须有一个类类型参数

  • 对于内置类型的运算符,不能改变其原本含义,像 int 类型的 + 号,语义不能被篡改。

  • 作为类成员函数重载时,形参数量比操作数少1**,因为第一个参数是隐藏的 this 指针。**

  • 有一些运算符不能重载,例如 . * 、 :: 、 sizeof 、 ? : (考点 ) 。

1.2 示例代码

以 Date 类为例,下面是重载 == 运算符的简单实现:

cpp 复制代码
cpp   
class Date 
{
public:
    Date(int year = 1900, 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;
}
 

二、赋值运算符重载

2.1 赋值运算符重载格式

赋值运算符重载函数一般具有以下特征:

  • 参数类型: const T& ,采用传递引用的方式,++能提高传参效率,避免对象拷贝。++

  • 返回值类型: T& ,返回引用可以提高返回效率,同时支持连续赋值,比如 a = b = c 。

  • 函数内部要检测是否是自己给自己赋值,避免不必要的操作(不建议暴力检查)。

  • 最后返回 *this ,以符合连续赋值的语义。

2.2 代码实现

cpp 复制代码
cpp   
class Date {
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}
    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;
};
 

2.3 注意事项

赋值运算符重载只能作为类的成员函数。这是因为如果用户没有显式实现,编译器会生成一个默认的赋值运算符重载。若在类外再实现一个全局的赋值运算符重载,就会与编译器生成的默认版本冲突。

三、前置++和后置++重载

3.1 前置++重载

前置 ++ ,即 ++a ,语义是先让对象自增,然后返回自增后的对象。由于 this 指向的对象在函数结束后不会销毁,所以可以以引用方式返回,提高效率。

cpp 复制代码
cpp   
class Date {
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}
    Date& operator++() {
        _day += 1;
        return *this;
    }
private:
    int _year;
    int _month;
    int _day;
};
 

3.2 后置++重载

后置 ++ ,即 a++ ,是先使用对象原来的值,然后再让对象自增。为了区分前置和后置 ++ ,C++ 规定后置 ++ 重载时多增加一个 int 类型的参数(这个参数仅用于区分,调用时不用传递,编译器自动处理)。由于要返回自增前的旧值,所以需要先保存 this 指向的对象,等对象自增后再返回保存的旧值,且只能以值的方式返回,不能返回引用。

cpp 复制代码
cpp   
class Date {
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}
    Date operator++(int) {
        Date temp(*this);
        _day += 1;
        return temp;
    }
private:
    int _year;
    int _month;
    int _day;
};
 

++前置++与后置++区别在于声明传参不同++

四、日期类的完整实现

4.1 获取某月天数

cpp 复制代码
cpp   
class Date {
public:
    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;
    }
    // 其他函数如构造函数、拷贝构造函数、各种运算符重载函数等...
private:
    int _year;
    int _month;
    int _day;
};
 

4.2 完整类定义

cpp 复制代码
cpp   
class Date {
public:
    // 获取某年某月的天数
    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;
    }
    // 全缺省的构造函数
    Date(int year = 1900, 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) {}
    // 赋值运算符重载
    Date& operator=(const Date& d) {
        if (this != &d) {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }
    // 析构函数
    ~Date() {}
    // 日期+=天数
    Date& operator+=(int day);
    // 日期+天数
    Date operator+(int day);
    // 日期-天数
    Date operator-(int day);
    // 日期-=天数
    Date& operator-=(int day);
    // 前置++
    Date& operator++();
    // 后置++
    Date operator++(int);
    // 后置--
    Date operator--(int);
    // 前置--
    Date& operator--();
    // >运算符重载
    bool operator>(const Date& d);
    // ==运算符重载
    bool operator==(const Date& d);
    // >=运算符重载
    bool operator>=(const Date& d);
    // <运算符重载
    bool operator<(const Date& d);
    // <=运算符重载
    bool operator<=(const Date& d);
    // !=运算符重载
    bool operator!=(const Date& d);
    // 日期-日期  返回天数
    int operator-(const Date& d);
private:
    int _year;
    int _month;
    int _day;
};
 

实现

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"Date.h"
// 日期+=天数
Date& Date::operator+=(int day)
{


	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			_month = 1;
			++_year;
		}
	}
	return *this;
}
// 日期+天数
Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp += day;
	/*tmp._day += day;
	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
	{
		tmp._day -= GetMonthDay(tmp._year, tmp._month);
		++tmp._month;
		if (tmp._month == 13)
		{
			tmp._month = 1;
			++tmp._year;
		}
	}*/
	return tmp;
}
// 日期-天数
Date Date::operator-(int day)
{
	Date tmp(*this);
	tmp._day -= day;
	while (tmp._day <= 0) 
	{
		--tmp._month;
		if (tmp._month == 0) 
		{
			tmp._month = 12;
			--tmp._year;
		}
		tmp._day += GetMonthDay(tmp._year, tmp._month);
	}
	return tmp;
}
// 日期-=天数
Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0) {
		--_month;
		if (_month == 0) {
			_month = 12;
			--_year;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
// 前置++
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
// 后置++
Date Date::operator++(int)
{
	Date tmp(*this);
	*this += 1;
	return tmp;
}
// 后置--
Date Date::operator--(int)
{
	Date tmp(*this);
	*this -= 1;
	return tmp;

}
// 前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
// >运算符重载
bool Date::operator>(const Date& d)
{
	return !((*this == d) || (*this < d));
}
// ==运算符重载
bool Date::operator==(const Date& d)
{
	return _year == d._year && _month > d._month && _day == d._day;
}
// >=运算符重载
bool Date::operator>=(const Date& d)
{
	return !(*this < d);
}
// <运算符重载
bool Date::operator<(const Date& d)
{
	if (_year > d._year)
	{
		return false;
	}
	if (_year == d._year && _month > d._month)
	{
		return false;
	}
	if (_year == d._year && _month > d._month && _day > d._day)
	{
		return false;
	}
	return true;
}
// <=运算符重载
bool Date::operator<=(const Date& d)
{
	return (*this == d) || (*this < d);
}
// !=运算符重载
bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}
// 日期-日期  返回天数
int Date::operator-(const Date& d)
{
	Date minDate = (*this < d) ? *this : d;
	Date maxDate = (*this < d) ? d : *this;
	int days = 0;
	while (minDate != maxDate) {
		minDate += 1;
		days++;
	}
	return days;
}

五、总结

通过对 Date 类各种运算符的重载实现,我们深入了解了C++ 运算符重载的机制和应用。合理运用运算符重载,可以让我们自定义的类使用起来更加直观、自然,就像使用C++ 内置类型一样方便。在实际项目中,根据具体需求灵活运用这些知识,能有效提升代码的质量和可维护性。希望这篇博客能帮助大家更好地掌握C++ 运算符重载这一重要特性。

相关推荐
爱coding的橙子10 分钟前
蓝桥杯备赛 Day 20 树基础
数据结构·c++·算法·蓝桥杯·深度优先
林麓27 分钟前
C++进阶笔记第一篇:程序的内存模型
开发语言·c++·笔记
Hello.Reader2 小时前
深入理解 C++ 内置数组(四十三)
开发语言·c++·算法
chase。2 小时前
ExternalProject_Add 使用手册与文档详解
c++
晨辰丷4 小时前
【STL】list介绍(附与vector的比较)
c语言·开发语言·数据结构·c++·list
今麦郎xdu_4 小时前
【数据结构】二叉搜索树
数据结构·c++·算法·stl
Word码4 小时前
[蓝桥杯] 挖矿(C&C++双语版)
c语言·数据结构·c++·笔记·算法·职场和发展·蓝桥杯
神里流~霜灭5 小时前
蓝桥备赛指南(14):树的直径与重心
c语言·数据结构·c++·算法·二叉树·深度优先·递归
今夜有雨.6 小时前
C-S模式之实现一对一聊天
linux·服务器·网络·c++·后端·tcp/ip·架构
鑫—萍7 小时前
数据结构与算法——链表OJ题详解(1)
c语言·开发语言·数据结构·c++·学习·算法·链表