1. 程式人生 > >c++日期類(Date類)

c++日期類(Date類)

1.Date中的建構函式

建構函式的作用可以說是對類變數的初始化(Init)如果我們不寫建構函式,系統會生成預設的建構函式,但是對於date類來說,這裡的預設建構函式什麼都不做,如果你建立了一個 Date d1,系統呼叫了預設建構函式,你會發現這裡的年月日都是隨機值,我們為了讓d1在初始化的時候就有一個確認的日期,所以得自己動手寫一個全預設的建構函式

我們在函式宣告中給一個確定的值,讓每個創建出來的日期類變數都有一個預設的初始值 1900,1,1


2.Date中的解構函式

解構函式的作用同構造函式是對應的,這裡是銷燬某些特定的成員變數,如我們用malloc申請出來的記憶體,如果不在解構函式中做特定的操作,就很可能導致記憶體洩漏,不過在Date中的成員並不需要特殊的去釋放他,他們生長在棧上,會隨函式棧楨的結束自動釋放掉,所以這裡就可以使用預設的解構函式,我們可以不用書寫

3.Date中的拷貝建構函式

建立物件的時候使用同類物件初始化,拷貝建構函式其實是一個建構函式的過載,如果我們沒有實現拷貝建構函式,系統也會自動生成拷貝建構函式,來依次拷貝類成員進行初始化

需要注意:拷貝建構函式必須使用傳引用的方式返回,否則會造成無窮遞迴

4.Date中的運算子過載

這裡我實現瞭如下幾個運算子的過載


需要注意的是

1.當返回一個Date類的變數時,我們使用傳值返回( 例如 + )還是傳引用返回(例如 +=)?

        可以這麼說,所有需要返回一個Date類的函式都可以使用傳值返回,可是傳值返回在返回的過程中會生成一個臨時變數,並且會呼叫一次建構函式,開銷比較大,所以在可以使用傳引用返回的情況下,我們最好有限考慮傳引用返回。

        傳引用返回的條件是,你要返回的Date變數在出了當前函式作用域還存在(例如 靜態變數static,或者是傳值傳進來的變數),只要生命週期大於這個函式的週期,就可以返回引用。當然還需要考慮這個變數應不應該被改變,例如使用 d1 + 1 的時候,d1是不需要被改變的,所以返回的值只可以是在類裡面建立的變數。使用 d1 += 1的時候,d1的值會被改變掉,所以就可以直接返回改變以後的d1

2.為了提高我們寫程式碼的效率,我們可以複用自己寫過的運算子

        例如,我們已經寫好了一個 ' < '的運算子,當我們需要些 ‘ >= ’的時候就可以直接使用 ‘ !< ’,這樣寫不僅僅可以提高我們寫程式碼的效率,還可以提高我們修改程式碼的效率,如果你有100函式都是用的相同邏輯,呼叫的是一個基礎的函式,如果這個邏輯出錯,修改的時候就只需要該基礎函式的邏輯

3.區分前置++(--)和後置++(--)

        相同點:兩者的運算子都是 ++,所以易於混淆

        不同點:因為本身難以區分,所以編譯器幫了我們很大的忙,在呼叫++(--)運算子時,編譯器當然可以辨別程式設計師輸入的是前置還是後置,如果是後置,編譯器在傳參的時候就會傳一個 int 型別的變數,在呼叫的時候如果發現什麼引數都沒有,那就預設是前置++(--),如果發現引數中有一個 int 型別的形參,那麼就會處理成後置的++(--),但是這裡的 int形參什麼事情都不做,只是為了區分前置和後置

下面我們看一下完整程式碼:

//Date.h
#pragma once

class Date
{
public:
    Date(int year = 1900,int month = 1,int day = 1);
    ~Date(){}
    bool IsValid();
    int GetMonthDay(int year,int month);
    bool IsLeapYear(int year);
    void Show();

    Date& 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);
    bool operator<(const Date& d);
    Date operator+(int day);
    Date& operator+=(int day);
    Date operator-(int day);
    Date& operator-=(int day);
    int operator-(const Date& d);//日期-日期 返回天數
    Date& operator++();//預設前置
    Date operator++(int);//用引數標誌後置++
    Date& operator--();
    Date operator--(int);

private:
    int _year;
    int _month;
    int _day;
};
//date.cpp
#include "date.h"
#include <iostream>
#include <assert.h>
using namespace std;

Date::Date(int year,int month,int day):_year(year),_month(month),_day(day)
{
    if(!IsValid())  // -> this.IsValid()
    {
        assert(false);
    }
}

bool Date::IsValid()
{
    return (_year > 0 
       && _month > 0 && _month <= 12 
       &&_day > 0 && _day <= GetMonthDay(_year,_month));
}

int Date::GetMonthDay(int year,int month)
{
    int arr[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
    if(month == 2 && IsLeapYear(year))
    {
        ++arr[2];
    }
    return arr[month];
}

bool Date::IsLeapYear(int year)
{
    return ((year % 4 == 0 && year % 100 != 0)
        || year % 400 == 0);
}

void Date::Show()
{
    cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
}

//  運算子過載經歷以下幾個步驟
//  (d1==d2)
//  d1.operator==(d2)
//  d1.operator==(&d1,d2)
//  bool Date::operator==(Date* this,const Date& d)
bool Date::operator==(const Date& d)
{
    if(_year == d._year && _month == d._month && _day == d._day)
        return true;
    return false;
}

//傳值返回:會多呼叫一次拷貝建構函式
//傳引用返回:直接返回,不需要呼叫拷貝構造
//            出了作用於變數還在,儘量使用傳引用返回
Date& Date::operator=(const Date& d)
{
    if(this != &d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    return *this;
}

bool Date::operator>=(const Date& d)
{
    return !(*this < d);
}

bool Date::operator<=(const Date& d)
{
    return ((*this < d) || (*this == d));
}
bool Date::operator!=(const Date& d)
{
    return !(*this == d);
}

bool Date::operator>(const Date& d)
{
    return !(*this <= d);
}

bool Date::operator<(const Date& d)
{
    if((_year < d._year) ||
       (_year == d._year && _month < d._month) ||
       (_year == d._year && _month == d._month && _day < d._day))
        return true;
    return false;
}

Date Date::operator+(int day)
{
    if(day < 0)
    {
        return (*this) - (-day);
    }
    Date d(*this);
    d._day += day;
    while(d.IsValid() == false)
    {
        int month_day = GetMonthDay(d._year,d._month);
        d._day -= month_day;
        ++d._month;
        if(d._month > 12)
        {
            d._month = 1;
            ++d._year;
        }
    }
    return d;
}

Date& Date::operator+=(int day)
{
    *this = (*this + day);
    return *this;
}

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

Date& Date::operator-=(int day)
{
    *this = (*this - day);
    return *this;
}

int Date::operator-(const Date& d)//日期-日期 返回天數
{
    int flag = 1;
    Date max = (*this);
    Date min = d;
    if((*this) < d)
    {
        max = d;
        min = (*this);
        flag = -1;
    }
    int count = 0;
    while(max != min)
    {
        ++min;
        ++count;
    }
    return flag*count;
}

//  ++d1 ->  d1.operator++(&d1);
Date& Date::operator++()//預設前置
{
    *this += 1; // 只調用一個函式
    //*this = *this + 1; //呼叫兩個函式,還要呼叫拷貝建構函式
    return *this;
}

//  d1++ ->  d1.operator++(&d1,0);
Date Date::operator++(int)//用引數標誌後置++
{
    Date tmp(*this);
    *this += 1;
    return tmp;
}

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

Date Date::operator--(int)
{
    Date tmp(*this);
    *this -= 1;
    return tmp;
}