吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1072|回复: 12
收起左侧

[C&C++ 原创] 【数据结构】C++实现日期基本类,重载运算符,友元..【第一篇】

[复制链接]
仿佛_一念成佛 发表于 2024-3-22 07:48
本帖最后由 仿佛_一念成佛 于 2024-3-22 08:16 编辑

C++实现日期基本类,重载运算符,友元..【第一篇】

最近刚好写完了关于c++的日期类实现。特地来分享给大家

话不多说,直接上示例

首先我要实现三个构造函数,一个拷贝函数和另外一个析构函数来构成最基本的日期类。在此之前我需要在我的类中声明private变量。

声明private变量

int dia //表示天数
int mes // 表示月份,
int anyo // 表示年份
char *mensaje // 表示一个字符串数组

声明默认函数

它们分别是
TCalendario(); // 默认构造函数,用来初始话日期为1/1/1900和mensaje为nullptr

TCalendario(int dia, int mes, int anyo, const char *mens); // 用来初始化日期为参数

TCalendario(const TCalendario &); // 深拷贝

~TCalendario(); //析构函数,用来释放内存

哦对了,类名为Tcalendario,相信聪明的你一定已经看出来了。

声明重载运算符

TCalendario &operator=(const TCalendario &); // 赋值运算符重载

TCalendario operator+(const int); // +号运算符重载

TCalendario operator-(const int); // -号运算符重载
TCalendario operator++(int); //后置++运算符重载

TCalendario &operator++(); // 前置++运算符重载

TCalendario operator--(int); // 后置--运算符重载
TCalendario &operator--(); // 前置--运算符重载

bool operator==(const TCalendario &) const; // 等号运算符重载

bool operator!=(const TCalendario &) const; //不等于号运算符重载

bool operator>(const TCalendario &) const; // 大于号运算符重载

bool operator<(const TCalendario &) const; //小于号运算符重载

一些其他函数的实现

bool ModFecha(int dia, int mes, int anyo); // 用来修改日期,True为修改成功,False为修改失败

bool EsVacio() const; // 用来判断当前对象是否具有初始化的值

int Dia() const; // 返回对象的当前天数

int Mes() const;// 返回对象的当前月份

int Anyo() const;// 返回对象的当前年份

char *Mensaje() const // 返回对象的当前数组字符

另外我们可以再加两个函数来判断日期是否正确OR闰年
int get_month_day(int month, int year) const;

bool check_days_correct(int day, int month, int year) const;


目前为止,我们已经声明了所有需要的函数,现在要对它们一一进行实现。声明函数放在.h文件中,实现函数放在.cpp文件中。


实现默认构造析构函数

默认构造函数

TCalendario::TCalendario() : dia(1), mes(1), anyo(1900), mensaje(nullptr) {} // 使用初始化列表来进行初始化

默认构造参数函数

TCalendario::TCalendario(int dia, int mes, int anyo, const char *mens) 
{
    if (check_days_correct(dia, mes, anyo)) // 首先需要判断日期是否正确,如为True,进行初始化
    {
        this->dia = dia;
        this->mes = mes;
        this->anyo = anyo;
        if (mens != nullptr) // 此处需要注意,我们正在使用一个指针来对char进行操作,需要先进行判断,然后动态分配char所需要的内存大小
        {
            // dynamic alloc memory to char
            size_t t_strlen = strlen(mens);
            this->mensaje = new char[t_strlen + 1];
            memset(this->mensaje, 0, t_strlen);
            strcpy(this->mensaje, mens);
        }
        else
        {
            this->mensaje = nullptr;
        }
    }

    else
    {
        // 日期错误,初始化为默认值
        this->dia = 1;
        this->mes = 1;
        this->anyo = 1900;
        this->mensaje = nullptr;
    }
}

日期判断函数

这一部分相信大家都看得懂就不打注释了


bool TCalendario::check_days_correct(int day, int month, int year) const
{
    int dia_ = get_month_day(month, year);
    if (day > dia_ || month > 12 || year < 1900 || day < 1 || month < 1)
    {
        return false;
    }
    else
    {
        return true;
    }
}
int TCalendario::get_month_day(int month, int year) const
{
    static int day[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) // check if year is bisiestor
    {
        if (month == 2)
        {
            return 29;
        }
    }
    return day[month];
}

析构函数(释放内存)

TCalendario::~TCalendario()
{
    // 在这里进行释放内存操作,重新将变量设置为默认
    this->anyo = 1900;
    this->mes = 1;
    this->dia = 1;
    if (mensaje != nullptr) // 此处需要特别注意,如果不加上这个空指针判断会造成释放空指针错误,从而导致程序崩溃。因为是一个ptr,所以要用delete[]
    {
        delete[] mensaje;
        mensaje = nullptr;
    }
    else
    {
        mensaje = nullptr;
    }
}

深拷贝

这里没有什么好说的,需要注意的是深拷贝带有const关键字。其他的都上默认构造参数函数一样

TCalendario::TCalendario(const TCalendario &t)
{
    if (check_days_correct(t.dia, t.mes, t.anyo))
    {
        // deep copy all value
        // cout << "deep copy" << endl;
        this->dia = t.dia;
        this->mes = t.mes;
        this->anyo = t.anyo;
        // // deep copy char
        if (t.mensaje != nullptr)
        {
            size_t t_strlen = strlen(t.mensaje);
            this->mensaje = new char[t_strlen + 1];
            memset(this->mensaje, 0, t_strlen);
            strcpy(this->mensaje, t.mensaje);
        }
        else
        {
            this->mensaje = nullptr;
        }
    }
    else
    {
        this->dia = 1;
        this->mes = 1;
        this->anyo = 1900;
        this->mensaje = nullptr;
    }
}

实现重载运算符

= 重载

TCalendario &TCalendario::operator=(const TCalendario &t)
{

    if (this == &t)
    {
        /* 如果出现当对象1赋值给对象1的情况下直接返回对象1,无需继续进行后续操作因为为同一个对象*/
        return *this;
    }
    /*在进行赋值前,需要对左值进行内存释放,不然会造成segmentation fault。在这里使用了析构函数来进行释放*/
    (*this).~TCalendario();

    /*下面的没什么可以讲的,都是一样的步骤,赋值,分配内存,然后结束*/
    if (check_days_correct(t.dia, t.mes, t.anyo))
    {
        this->dia = t.dia;
        this->mes = t.mes;
        this->anyo = t.anyo;
        // // deep copy char
        if (t.mensaje != nullptr)
        {
            size_t t_strlen = strlen(t.mensaje);
            this->mensaje = new char[t_strlen + 1];
            memset(this->mensaje, 0, t_strlen);
            strcpy(this->mensaje, t.mensaje);
        }
        else
        {
            this->mensaje = nullptr;
        }
    }
    else
    {
        this->dia = 1;
        this->mes = 1;
        this->anyo = 1900;
        this->mensaje = nullptr;
    }
    return *this;
}

+ 重载

这里需要特别注意,这里没有使用引用进行返回,而是创建了一个临时对象

TCalendario TCalendario::operator+(const int value)
{
    // 创建临时对象
    TCalendario temp(*this);
    if (value < 0) // 当传入的参数小于0无需进行任何操作
    {
        return temp;
    }
    // 这里进行了+操作,首先对临时对象的dia变量进行了相加,然后进入一个while循环。后面就是进行加月份或者年份操作当当前日期超出的时候
    temp.dia += value;
    while (temp.dia > get_month_day(temp.mes, temp.anyo))
    {
        temp.dia -= get_month_day(temp.mes, temp.anyo);
        ++temp.mes;
        if (temp.mes > 12)
        {
            temp.mes = 1;
            ++temp.anyo;
        }
    }
    return temp;
}

- 重载

和上面的+一样,不过逻辑需要变动一下,直接看代码吧

TCalendario TCalendario::operator-(const int value)
{
    TCalendario temp(*this);
    if (value < 0)
    {
        return temp;
    }
    temp.dia -= value;
    while (temp.dia <= 0)
    {
        --temp.mes;
        if (temp.anyo <= 1900) // when data is inferior than 1900 reinicialice to init value and break loop
        {
            temp.~TCalendario();
            break;
        }
        if (temp.mes == 0)
        {
            temp.mes = 12;
            --temp.anyo;
        }
        temp.dia += get_month_day(temp.mes, temp.anyo);
    }
    return temp;
}

++ 后置重载

和上方的一样,不过这里是后置++,有一个占位符int来表示后置

TCalendario TCalendario::operator++(int)
{
    TCalendario temp = *this;

    ++dia;
    while (dia > get_month_day(mes, anyo))
    {
        dia -= get_month_day(mes, anyo);
        ++mes;
        if (mes > 12)
        {
            mes = 1;
            ++anyo;
        }
    }

    return temp;
}

++ 前置重载

此处开始变了,返回的不再是临时对象而是引用。逻辑还是一样的,只不过返回对象变了


TCalendario &TCalendario::operator++()
{
    ++this->dia;
    while (this->dia > get_month_day(this->mes, this->anyo))
    {
        this->dia -= get_month_day(this->mes, this->anyo);
        ++this->mes;
        if (this->mes > 12)
        {
            this->mes = 1;
            ++this->anyo;
        }
    }
    return *this;
}

-- 后置重载

返回临时对象。当超出初始范围时重新赋值。其他逻辑不变

TCalendario TCalendario::operator--(int)
{
    if (this->dia == 1 && this->mes == 1 && this->anyo == 1900)
    {
        this->~TCalendario();
        return *this;
    }

    TCalendario temp(*this);
    --dia;
    while (dia <= 0)
    {
        --mes;
        if (anyo <= 1900) // when data is inferior than 1900 reinicialice to init value and break loop
        {
            this->~TCalendario();
            break;
        }
        if (mes == 0)
        {
            mes = 12;
            --anyo;
        }
        dia += get_month_day(mes, anyo);
    }
    return temp;
}

-- 前置重载

返回引用,逻辑不变

TCalendario &TCalendario::operator--()
{
    if (this->dia == 1 && this->mes == 1 && this->anyo == 1900)
    {
        this->~TCalendario();
        return *this;
    }

    --this->dia;
    while (this->dia <= 0)
    {
        --this->mes;
        if (this->anyo <= 1900) // when data is inferior than 1900 reinicialice to init value and break loop
        {
            this->~TCalendario();
            break;
        }
        if (this->mes == 0)
        {
            this->mes = 12;
            --this->anyo;
        }
        this->dia += get_month_day(this->mes, this->anyo);
    }
    return *this;
}

== 重载

返回为布尔值。此函数功能为用来比较两个对象是否是同一个值。

在这里有一个坑,当时写的时候花了有些时间才发现。

当对象中的mensaje为nullptr的时候,我一开始用的是strcmp来比较两个是否相等。那么大家肯定知道,我一个空指针和一个字符比较那么肯定会报错。所以这里我使用了一个string对象来初始化一个空的字符串然后去进行compare函数比较,这样子就没有什么问题了

bool TCalendario::operator==(const TCalendario &t) const
{
    string this_mensaje;
    string t_mensaje;
    if(this->mensaje == nullptr)
    {
        this_mensaje = "";
    }
    else
    {
        this_mensaje = this->mensaje;
    }

    if(t.mensaje == nullptr)
    {
        t_mensaje = "";
    }
    else
    {
        t_mensaje = t.mensaje;
    }
    return ((this->dia == t.dia) && (this->mes == t.mes) && (this->anyo == t.anyo) && (this_mensaje.compare(t_mensaje) == 0));
}

!= 重载

和==一样,不过是相反的条件

bool TCalendario::operator!=(const TCalendario &t) const
{
    string this_mensaje;
    string t_mensaje;
    if(this->mensaje == nullptr)
    {
        this_mensaje = "";
    }
    else
    {
        this_mensaje = this->mensaje;
    }

    if(t.mensaje == nullptr)
    {
        t_mensaje = "";
    }
    else
    {
        t_mensaje = t.mensaje;
    }

    return ((this->dia != t.dia) || (this->mes != t.mes) || (this->anyo != t.anyo) || (this_mensaje.compare(this_mensaje) != 0));

}

> 重载

此处有两个条件来判断对象1是否大于对象2

  1. 日期大于
  2. 日期等于但是mensaje大于
bool TCalendario::operator>(const TCalendario &t) const
{
    // (Criterio 1) : la fecha de T1 es posterior a la de T2(independientemente del mensaje).
    // (Criterio 2) : la fecha de T1 es igual a la de T2, y el mensaje de T1 es mayor(en comparación de cadenas) al mensaje de T2.Si la fecha de T1 es igual a la de T2 y el mensaje de T1 e igual al de T2, entonces tanto operator> como operator<devuelven FALSE.
    string this_mensaje;
    string t_mensaje;
    if(this->mensaje == nullptr)
    {
        this_mensaje = "";
    }
    else if(t.mensaje == nullptr)
    {
        t_mensaje = "";
    }
    else
    {
        this_mensaje = this->mensaje;
        t_mensaje = t.mensaje;
    }
    bool crt_1 = (this->anyo > t.anyo) || (this->anyo == t.anyo && this->mes > t.mes) || (this->anyo == t.anyo && this->mes == t.mes && this->dia > t.dia);

    // crt_2 have more priority than crt_1
    bool crt_2 = ((this->anyo == t.anyo && this->mes == t.mes && this->dia == t.dia) && this_mensaje.compare(t_mensaje) > 0);

    return crt_1 || crt_2;  
}

< 重载

相同的逻辑不同的条件


bool TCalendario::operator<(const TCalendario &t) const
{
    string this_mensaje;
    string t_mensaje;
    if (this->mensaje == nullptr)
    {
        this_mensaje = "";
    }
    else if (t.mensaje == nullptr)
    {
        t_mensaje = "";
    }
    else
    {
        this_mensaje = this->mensaje;
        t_mensaje = t.mensaje;
    }
    bool crt_1 = (this->anyo < t.anyo) || (this->anyo == t.anyo && this->mes < t.mes) || (this->anyo == t.anyo && this->mes == t.mes && this->dia < t.dia);

    // crt_2 have more priority than crt_1
    bool crt_2 = ((this->anyo == t.anyo && this->mes == t.mes && this->dia == t.dia) && this_mensaje.compare(t_mensaje) < 0);

    return crt_1 || crt_2;
}

那么到这里已经全部实现了个别运算符重载和默认函数的实现。接下来实现一些其他函数or方法也可以这么说

其他函数

修改日期

返回True 修改成功。反之失败


bool TCalendario::ModFecha(int dia, int mes, int anyo)
{
    /*Check de Numero de días correcto, número de mes correct y Control de años bisiestos*/

    if (dia > 31 || dia < 1 || mes < 1 || mes > 12 || anyo < 1900)
    {
        return false;
    }

    int dia_ = get_month_day(mes, anyo);
    if (dia <= dia_)
    {
        this->dia = dia;
        this->mes = mes;
        this->anyo = anyo;
        return true;
    }
    return false;
}

修改mensaje

返回True 修改成功。反之失败


bool TCalendario::ModMensaje(const char *c)
{
    /*mensaje is null we need to allocated memory to it*/
    if (c == nullptr)
    {
        this->mensaje = nullptr;
        return true;
    }

    if (this->mensaje == nullptr)
    {
        // alloc new memory
        size_t t_strlen = strlen(c);
        this->mensaje = new char[t_strlen + 1];
        memset(this->mensaje, 0, t_strlen);
        strcpy(this->mensaje, c);
        return true;
    }
    /*delete old char memory and alloc new memory*/
    else
    {
        delete[] this->mensaje;
        size_t t_strlen = strlen(c);
        this->mensaje = new char[t_strlen + 1];
        memset(this->mensaje, 0, t_strlen);
        strcpy(this->mensaje, c);
        return true;
    }
    /*unusual condicional return*/
    return false;
}

判断当前对象日期是否为默认值

根据声明的函数来实现。很简单的逻辑


bool TCalendario::EsVacio() const 
{
    /* devuelve TRUE si el calendario tiene fecha 1/1/1900 y el mensaje es NULL */
    if ((this->anyo == 1900 && this->dia == 1 && this->mes == 1) && (this->mensaje == nullptr))
    {
        return true;
    }
    return false;
}

接下来是几个返回天数,月份,年份,信息的函数

天数


int TCalendario::Dia() const
{
    return this->dia;
}

月份

int TCalendario::Mes() const
{
    return this->mes;
}

年份

int TCalendario::Anyo() const
{
    return this->anyo;
}

信息

唯一需要注意的就是这个了,不好好判断会有空指针错误。然后程序就崩了

char *TCalendario::Mensaje() const
{

    if (this->mensaje == nullptr)
    {
        return nullptr;
    }
    else
    {
        return this->mensaje;
    }
}

友元 【<<重载】

终于到最后了,友元还是蛮简单的实现方法,和cout输出一样,只是它换位另外一种输出方式了。

声明

一般我放在类的最上方,要求为public,不能为private

friend std::ostream &operator<<(std::ostream &, const TCalendario &);

实现

跟cout差不多,不过要带两个参数,一个为输出流对象,另外一个为const对象。内部实现就是进行了一些格式化的输出,没什么可以讲的

ostream &operator<<(ostream &cout, const TCalendario &t)
{
    if (t.dia < 10)
    {
        cout << "0";
    }
    cout << t.dia << "/";
    if (t.mes < 10)
    {
        cout << "0";
    }
    cout << t.mes << "/" << t.anyo << " ";

    if (t.mensaje == nullptr)
    {
        cout << "\"\"";
    }
    else
    {
        cout << "\""<< t.mensaje << "\"";
    }

    return cout;
}

结束

到这里第一部分就写完了,这个作业在写的过程中还是花费了一些时间的。不过好在最后还是完成了。难度为c++初学者中等。

后面我将实现日期类的Vector类,自己手搓一个仿vector的方法出来,实现了resize()。

那么大家下次再见!

免费评分

参与人数 4威望 +1 吾爱币 +23 热心值 +4 收起 理由
爱飞的猫 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
woyucheng + 1 + 1 谢谢@Thanks!
seeyou_shj + 1 + 1 谢谢@Thanks!
满不懂 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

爱飞的猫 发表于 2024-3-22 08:06
仿佛_一念成佛 发表于 2024-3-22 08:03
我从vscode直接复制的排版,改了好几次了不知道为什么改不回来

因为两边的 Markdown 渲染实现不一样,所以会造成这种情况。

你文中的 ``` 可能没被正确识别出来,被合并到之前的列表里面了。在这行之前加一个空行可能会改善这个情况。
爱飞的猫 发表于 2024-3-22 08:00
排版乱了哦!代码块建议前后空一行避免识别错误。

此外,自 C++20 开始 <chrono> 自带了相关的实现,可以参考实现/使用。

caojian1624 发表于 2024-3-22 07:57
 楼主| 仿佛_一念成佛 发表于 2024-3-22 08:03
爱飞的猫 发表于 2024-3-22 08:00
排版乱了哦!代码块建议前后空一行避免识别错误。

[md]此外,自 C++20 开始 [``](https://zh.cppreferen ...

我从vscode直接复制的排版,改了好几次了不知道为什么改不回来

点评

因为两边的 Markdown 渲染实现不一样,所以会造成这种情况。 你文中的 ``` 可能没被正确识别出来,被合并到之前的列表里面了。在这行之前加一个空行可能会改善这个情况。  详情 回复 发表于 2024-3-22 08:06
 楼主| 仿佛_一念成佛 发表于 2024-3-22 08:12
爱飞的猫 发表于 2024-3-22 08:06
因为两边的 Markdown 渲染实现不一样,所以会造成这种情况。

你文中的 ``` 可能没被正确识别出来,被 ...

从<重载往后面的就开始奇怪了,明明都和前面的是一样的格式哇。这咋识别不出来呢
 楼主| 仿佛_一念成佛 发表于 2024-3-22 08:17
爱飞的猫 发表于 2024-3-22 08:06
因为两边的 Markdown 渲染实现不一样,所以会造成这种情况。

你文中的 ``` 可能没被正确识别出来,被 ...

找到那个列表了。感谢!
zsj118106 发表于 2024-3-22 08:27
学习了,大佬牛比
头像被屏蔽
wuaipojie92 发表于 2024-3-22 08:41
提示: 作者被禁止或删除 内容自动屏蔽
水到渠成 发表于 2024-3-22 08:55
学习一下
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 17:08

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表