全文カタログ
序章
クラスとオブジェクト 1 (初期知識)
クラスとオブジェクト 2 (デフォルトのメンバー関数)
クラスとオブジェクト 3 (初期化リスト)
クラスとオブジェクト 4 (静的、const、フレンド)
クラスとオブジェクトの基本を理解した後、クラスを作成します。簡単に実装できます (以前の知識の紹介リンクは上にあり、いくつかの基本的な知識はこの記事では説明しません)。
日付クラスを実装する
概要
まず、インターネット上にある日付計算ツールの機能を見てみましょう。2
つの日付の差を計算したり、日付から日数を加算または減算したりできます。
クラスとオブジェクトを学習する前に、日付の年、月、日を構造体に格納し、カプセル化関数を使用して日付構造体に対する操作を実装しますが、今回は、日付をオブジェクトの属性としてカプセル化できるようになりました
。クラス型。日付クラスの操作は便宜上、クラス メソッドとしてカプセル化されています。
もちろん、上記 2 つの機能を実現するには、2 つの日付が等しいかどうかを判断したり、2 つの日付を比較したりするメソッドを実装する必要があります。もちろん、日付を出力するメソッドも必要です。次に、1 つずつ実装していきます。
まずクラス型を定義します。
class Date
{
private:
int _year;
int _month;
int _day;
};
デフォルトのメンバー関数
まず、4 つのデフォルトのメンバー関数を実装しましょう
コンストラクタ
この日付クラスには組み込み型しかないため、コンパイラーによって生成された引数のないデフォルトのメンバー関数は初期化を実装できません。完全にデフォルトのデフォルト コンストラクターを実装して、パラメーターを渡さずにクラス オブジェクトを定義したり、パラメーターを渡したりすることができます。
// 全缺省的构造函数
Date::Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{
}
初期化リストでメンバー変数の初期化を実装します。
デストラクター
実際、日付クラスには動的に開かれるリソースがないため、コンパイラによって自動的に生成されるデストラクターで十分ですが、ここでまだ認識されているのは、デストラクターでメンバー変数を 0 に設定できることです。
// 析构函数
Date::~Date()
{
_year = 0;
_month = 0;
_day = 0;
}
コピー構築
コピー コンストラクターはコンストラクターのオーバーロードであり、パラメーターはクラス オブジェクトへの参照です。
// 拷贝构造函数
// d2(d1)
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
const を使用してパラメータを変更すると、const オブジェクトを新しいクラス オブジェクトに値を割り当てるパラメータとしても使用できるようになります。
代入のオーバーロード
代入演算子のオーバーロードは単なるoperator
関数の=
オーバーロードです。明示的なパラメータは、const Date&
const によって変更できるオブジェクトです。
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
関数内で、(このポインターが指す) オブジェクトのメンバー変数を d のメンバー変数に代入します。
連続代入を実現するために、この関数の戻り値はクラス型 return への参照であることに注意してください。*this
たとえば
、いくつかの組み込み型変数が連続的に代入されます。
a = b = c = d;
このようなコードでは、d を c に代入した結果が b に代入され、その結果が a に代入されます。
したがって、代入演算子のオーバーロードの場合、この関数は = の前のオペランド、つまり最初のパラメーターの参照、つまり return を返す必要があります*this
。
関数演算子のオーバーロード
デフォルトのメンバー関数を使用して、メンバー関数の実装を続けます。
日付間の比較
日付比較は次のようにオーバーロードできます。==、!=、>、>=、<、<=
これらの関数のパラメータ リストは同じである必要があり、それらはすべて暗黙的なthis
ポインタとクラス参照でありDate&
、
戻り値は bool で、式が true の場合は true を返し、それ以外の場合は false を返します。
ただし、このタイプの関数ではオブジェクトの値を変更する必要はないため、このポインターが指すオブジェクトとクラス オブジェクトの参照は const で変更できます。
//==重载举例:
bool operator==(const Date& d) const;
実装する場合、2 つの日付の大小関係は、関数内の 2 つのオブジェクトのメンバー変数を判断することによって取得されます。
- == 演算子のオーバーロード
// ==运算符重载
bool Date::operator==(const Date& d)const
{
if ((_year == d._year)
&& (_month == d._month)
&& (_day == d._day))
{
return true;
}
else
{
return false;
}
}
- != 演算子のオーバーロード
// !=运算符重载
bool Date::operator!=(const Date& d)const
{
if (!(*this == d))
{
return true;
}
return false;
}
- > 演算子のオーバーロード
// >运算符重载
bool Date::operator>(const Date& d)const
{
if (_year > d._year)
{
return true;
}
if (_year == d._year && _month > d._month)
{
return true;
}
if (_year == d._year && _month == d._month && _day > d._day)
{
return true;
}
return false;
}
- >= 演算子のオーバーロード
// >=运算符重载
bool Date::operator>=(const Date& d)const
{
if (*this > d || *this == d)
{
return true;
}
return false;
}
- < 演算子のオーバーロード
// <运算符重载
bool Date::operator<(const Date& d)const
{
if (_year < d._year)
{
return true;
}
if (_year == d._year && _month < d._month)
{
return true;
}
if (_year == d._year && _month == d._month && _day < d._day)
{
return true;
}
return false;
}
- <= 演算子のオーバーロード
// <=运算符重载
bool Date::operator<=(const Date& d)const
{
if (*this < d || *this == d)
{
return true;
}
return false;
}
日付 += および + 日
- 日付 += 日数
日付 += 日数、つまり、日付オブジェクト (this によって暗黙的に示される) の特定の日数後の日付を計算し、オブジェクトの参照を返します。+= 演算子は明らかに日付オブジェクトのメンバー変数を変更するため、 const では変更できません。
Date& operator+=(int day);
_day
実装する場合、最初に date オブジェクトのメンバーに追加できますday
。値が現在の値より大きい
場合は、現在の月の日数を減算し、値が次の場合は1 を加算します (1 か月遡ります)。12 より大きい場合は、「1 に設定、1 を加算 (1 年前に戻る)」を繰り返し、値が現在の月の日数より小さくなるまで、最後の参照が返されます。_day
_month
_day
_month
_month
_year
_day
*this
次に、特定の月の日数を取得する関数をカプセル化します (12 か月の日数を配列に格納し、対応する月の日数を返します)。
// 获取某年某月的天数(用于日期加减天数)
int Date::GetMonthDay(int year, int month)
{
int monthdays[13] = {
0,31,28,31,30,31,30,31,31,30,31,30,31 };
if ((month == 2) && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
monthdays[month] = 29;
}
return monthdays[month];
}
その後、+= 演算子をオーバーロードできます:
パラメーターとして渡される日数は負の値になる可能性があることに注意してください。これは、日付から -day を引いたものになります。このとき、呼び出しには -= を使用するだけです(近日実装予定)
// 日期+=天数
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 > 12)
{
_year++;
_month = 1;
}
}
return *this;
}
- 日付+日数
日付 + 日数では日付クラスの日付を変更できないため、このポインターが指すオブジェクトに const 変更を追加できます。
Date operator+(int day)const;
関数内で、一時的な日付クラス オブジェクトを作成しtemp
、*this
それを使用して初期化します (コピー構築)。
その後、temp+=day;
上記で実装されたオーバーロードを呼び出し+=
、
最後に を返しますtemp
。
ここでの temp はスコープを出た後に破棄されるため、ここでの戻り値は参照ではなく値である必要があることに注意してください(ただし、値で返すとコピー構築が呼び出されます)。
// 日期+天数
Date Date::operator+(int day)const
{
Date temp(*this);//拷贝构造
temp += day;//复用+=运算符重载
return temp;//返回临时值,不改变*this的值
}
日付-=と日数
- 日付 -= 日数
date-=days の実装は += に似ています。
まず_day
減算しますday
。
減算したday
値_day
が 0 未満の場合は、_day
当月の前月の_month-1
日数( ) を加算します。値が 0 未満の
場合は、 _month
、_month
12 に設定します_year
。 の値が0 より大きくなる
まで_day
、値は 1 ずつ減分され、
最後に参照が返されます*this
。
// 日期-=天数
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day < 1)
{
_month--;
_day += GetMonthDay(_year, _month);//如果--后的_month为0,函数返回0,不影响结果
if (_month <= 0)
{
_year--;
_month = 13;
}
}
return *this;
}
実装で注意すべき点は、値の値が1
の場合、その月の日数を加算()する必要があり、この時に渡される値は0ですが、実装しているので、 month 配列で添字 0 が付いているデータは 0 であり、影響を与えないため、最終結果には影響しません; 0 未満の場合は、それを解決するメソッドを呼び出すだけです。_month
_day
_month-1
GetMonthDay
GetMonthDay
_day-0
_day
_day
+= -day
- 日付 - 日数
date-days は + と同様に実装され、一時オブジェクトの作成がtemp
必要で、参照ではなく値を返す必要があります。
// 日期-天数
Date Date::operator-(int day)const
{
Date temp;
temp -= day;
return temp;
}
日付の接頭辞++と後置++
++
つまり+=1
、わかりやすいので、この関数では上記のオーバーロードを呼び出すだけで済みます+=1
。
pre-++ と post-++ の違いに注意してください。pre-++ の後は +=1、式の値は +1 後の値になり、post-++ の後は + になります。 =1、式の値は +1 前の値です。
++ では、オペランドはクラス オブジェクトであり、プリロードされた ++ バージョンの戻り値は +1 後の参照である必要があり、ポスト バージョンの戻り値はインクリメント前のオブジェクトの値である必要があります。
オーバーロードを実現するには、演算子のパラメーター リストを区別する方法を見つけるだけで済みます。演算子が
2 つのオペランドをオーバーロードする場合、関数の最初のパラメーターは左側のオペランドに対応し、2 番目のパラメーターは右側のオペランドに対応します。たとえば、 == のオーバーロード:bool operator==(const Date& d);
最初のパラメーターは implicit でthis
、2 番目のパラメーターは ですconst Date& d
。このオーバーロードされた関数を呼び出すことができます。d1 == d2;
この呼び出しは と同等ですd1.operator==(d2);
。
- プレフィックス++
プレフィックス ++ は、プレフィックス ++ のオペランドに対応する最初のパラメータとして this ポインタを暗黙的に渡すことしかできません
。インクリメントされた日付オブジェクトが返されるため、オブジェクトの参照を返すだけで十分です。
// 前置++
Date& Date::operator++()
{
*this += 1;
return *this;
}
pre-++ を使用する場合、これはこのオーバーロードされた関数を呼び出すことと同じです。d.operator++();
- リア++
++ 後の実装と ++ 前の実装を区別するために、暗黙的な this ポインターに加えて、パラメーター リストに未使用の int が存在することもあります。※thisはpost ++の第1オペランドに相当し、intは第2オペランドに相当します(ただし、ここでのintは意味を区別するためだけで使用しません); インクリメント前の日付オブジェクトが返されるので
、一時オブジェクト temp は *this で初期化され、元のオブジェクトがインクリメントされて temp に返されます。ここでは、参照の戻りの代わりに値の戻りが必要です(前と同じ理由により)。
// 后置++
Date Date::operator++(int)
{
Date temp(*this);
*this += 1;
return temp;
}
postfix ++ を使用する場合、これはこのオーバーロードされた関数を呼び出すことと同じです。d.operator++(0);
日付の接頭辞- - と接尾辞- -
pre- - - と post- - の実装と区別は ++ に似ており、関数内で -= を使用し、追加の int パラメータで区別します。
- 正面- -
// 前置--
Date& Date::operator--()
{
*this -= 1;
return *this;
}
参照はデクリメントされたオブジェクトを返します。これは次と同等です。d.operator--();
- 後方 - -
// 后置--
Date Date::operator--(int)
{
Date temp(*this);
*this -= 1;
return *this;
}
Value はデクリメント前のオブジェクトを返します。これは次と同等です。d.operator--(0);
日付 - 日付
-
Date - Date は、2 つの日付の間の日数を返すオーバーロードです。
暗黙的な this ポインターとクラス オブジェクトへの参照の 2 つのパラメーター リストがあります。2 つの日付の間の日数を計算するときに日付オブジェクトは変更されないため、*this とオブジェクトへの参照はすべて const で変更されます(以前に比較演算子がオーバーロードされたときと同じなので、ここでは繰り返しません) );
戻り値は int で、日数の差を示します。
int operator-(const Date& d)const;
この関数を実装する場合、小さい日付を直接増分して増分の数を記録し、2 つの日付が等しいことを確認して、カウンター変数の値を返すことができます。
元のオブジェクトの値は変更できないため、 count をインクリメントするために一時オブジェクト temp が作成されることに注意してください。
// 日期-日期 返回天数
int Date::operator-(const Date& d)const
{
int day = 0;
Date temp(*this);
if (temp > d)
{
while (temp != d)
{
--temp;
day++;
}
}
else
{
while (temp != d)
{
++temp;
day--;
}
}
return day;
}
入力と出力のオーバーロード (友達)
上記のメンバー関数を実装すると、クラスオブジェクトの加算、減算、乗算、除算、比較などが組み込み型と同様に使用できるようになり、利便性と可読性が大幅に向上します>>
。<<
過負荷でもありますか?もちろんそれは可能です:
cin
およびcout
標準入力オブジェクトと標準出力オブジェクトであるため、cin >>
標準入力ストリームからデータを読み取ったり、cout <<
標準出力ストリームにデータを出力したりできます。それらのクラス タイプはistream
とですostream
。
このような知識があると、>> 演算子の左側のオペランドが cin に対応し、右側のオペランドが入力するクラス オブジェクトに対応する限り、>> のオーバーロードを実現できます。同様に、左側の演算も同様です。 of << 数値は cout に対応し、右側のオペランドは出力されるクラス オブジェクトに対応するため、<< をオーバーロードできます。
したがって、>>
オーバーロードの最初のパラメーターの型はistream&
、2 番目のパラメーターの型はDate&
、<<
オーバーロードされる最初のパラメーターの型はostream&
、2 番目のパラメーターの型は である必要がありますconst Date&
。
ただし、クラスのメンバー関数の場合、最初のパラメーターは暗黙的なパラメーター受け渡し用の固定 this ポインターである必要があるため、メンバー関数に対して上記のパラメーター受け渡しメソッドを実装することは不可能になります。
パラメーターを渡す上記のメソッドを実装するには、それが非メンバー関数である必要があり、関数内のメンバー変数にアクセスできる必要があります。このような要件の下では、 を使用してfriend
外部関数をこのクラスのフレンド関数として宣言し、暗黙的な this がなくメンバー変数にアクセスできるようにすることができます。
継続的な入出力を実現するために、式の値は次のようになります。ストリームなので、オーバーロードされると、関数の戻り値はistream&
または になりますostream&
。
//>>重载
istream& operator>>(istream& in, Date& d)
{
int year = 0;
int month = 0;
int day = 0;
in >> year >> month >> day;
if (month < 1 || month>12 || day<1 || day>Date::GetMonthDay(year, month))
{
cout << "输入错误" << endl;
assert(0);
}
else
{
d._year = year;
d._month = month;
d._day = day;
}
return in;
}
//<<重载
ostream& operator<<(ostream& out, const Date& d)
{
if (d._month < 1 || d._month>12 || d._day<1 || d._day>Date::GetMonthDay(d._year, d._month))
{
cout << "输入错误" << endl;
}
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
もちろん、クラス内にステートメントを追加する必要もありますfriend
。
この時点で、日付クラスのすべてのメンバー関数が実装され、一般的なコードが後ろにあるので、この日付クラスをテストするプログラムを書くこともできます。
コードの概要
ヘッドファイル
//头文件
#include<iostream>
#include<cassert>
using namespace std;
class Date
{
//友元输入输出
friend istream& operator>>(istream& in, Date& d);
friend ostream& operator<<(ostream& out, const Date& d);
public:
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 拷贝构造函数
// d2(d1)
Date(const Date& d);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d);
// 析构函数
~Date();
// 获取某年某月的天数
static int GetMonthDay(int year, int month);
// 日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day)const;
// 日期-=天数
Date& operator-=(int day);
// 日期-天数
Date operator-(int day)const;
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 前置--
Date& operator--();
// 后置--
Date operator--(int);
// >运算符重载
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;
// 日期-日期 返回天数
int operator-(const Date& d)const;
//打印
void printDate()const;
private:
int _year;
int _month;
int _day;
};
istream& operator>>(istream& in, Date& d);
ostream& operator<<(ostream& out, const Date& d);
ソースファイル
//源文件
#include"data.h"
// 全缺省的构造函数
Date::Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{
}
// 拷贝构造函数
// d2(d1)
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
// 析构函数
Date::~Date()
{
_year = 0;
_month = 0;
_day = 0;
}
// 获取某年某月的天数(用于日期加减天数)
int Date::GetMonthDay(int year, int month)
{
int monthdays[13] = {
0,31,28,31,30,31,30,31,31,30,31,30,31 };
if ((month == 2) && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
monthdays[month] = 29;
}
return monthdays[month];
}
// 日期+=天数
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 > 12)
{
_year++;
_month = 1;
}
}
return *this;
}
// 日期+天数
Date Date::operator+(int day)const
{
Date temp(*this);//拷贝构造
temp += day;//复用+=运算符重载
return temp;//返回临时值,不改变*this的值
}
// 日期-=天数
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day < 1)
{
_month--;
_day += GetMonthDay(_year, _month);//如果--后的_month为0,函数返回0,不影响结果
if (_month <= 0)
{
_year--;
_month = 13;
}
}
return *this;
}
// 日期-天数
Date Date::operator-(int day)const
{
Date temp;
temp -= day;
return temp;
}
// 前置++
Date& Date::operator++()
{
*this += 1;
return *this;
}
// 后置++
Date Date::operator++(int)
{
Date temp(*this);
*this += 1;
return temp;
}
// 前置--
Date& Date::operator--()
{
*this -= 1;
return *this;
}
// 后置--
Date Date::operator--(int)
{
Date temp(*this);
*this -= 1;
return *this;
}
// ==运算符重载
bool Date::operator==(const Date& d)const
{
if ((_year == d._year)
&& (_month == d._month)
&& (_day == d._day))
{
return true;
}
else
{
return false;
}
}
// >运算符重载
bool Date::operator>(const Date& d)const
{
if (_year > d._year)
{
return true;
}
if (_year == d._year && _month > d._month)
{
return true;
}
if (_year == d._year && _month == d._month && _day > d._day)
{
return true;
}
return false;
}
// >=运算符重载
bool Date::operator>=(const Date& d)const
{
if (*this > d || *this == d)
{
return true;
}
return false;
}
// <运算符重载
bool Date::operator<(const Date& d)const
{
if (_year < d._year)
{
return true;
}
if (_year == d._year && _month < d._month)
{
return true;
}
if (_year == d._year && _month == d._month && _day < d._day)
{
return true;
}
return false;
}
// <=运算符重载
bool Date::operator<=(const Date& d)const
{
if (*this < d || *this == d)
{
return true;
}
return false;
}
// !=运算符重载
bool Date::operator!=(const Date& d)const
{
if (!(*this == d))
{
return true;
}
return false;
}
// 日期-日期 返回天数
int Date::operator-(const Date& d)const
{
int day = 0;
Date temp(*this);
if (temp > d)
{
while (temp != d)
{
--temp;
day++;
}
}
else
{
while (temp != d)
{
++temp;
day--;
}
}
return day;
}
//打印
void Date::printDate()const
{
cout << _year << " " << _month << " " << _day << endl;
}
istream& operator>>(istream& in, Date& d)
{
int year = 0;
int month = 0;
int day = 0;
in >> year >> month >> day;
if (month < 1 || month>12 || day<1 || day>Date::GetMonthDay(year, month))
{
cout << "输入错误" << endl;
assert(0);
}
else
{
d._year = year;
d._month = month;
d._day = day;
}
return in;
}
ostream& operator<<(ostream& out, const Date& d)
{
if (d._month < 1 || d._month>12 || d._day<1 || d._day>Date::GetMonthDay(d._year, d._month))
{
cout << "输入错误" << endl;
}
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
メイン機能
//main函数
int main()
{
Date d1;
cin >> d1;
cout << d1;
Date d2(2023, 5, 20);
cout << d2;
cout << d2 - d1 << "天" << endl;
return 0;
}
要約する
この時点で、date クラスの実装が導入されます。date クラスを実装することで、クラスとオブジェクトについての理解がさらに深まったと思います。
同時に、クラスとオブジェクトの基礎知識も導入されました。C
++ の旅はまだまだ続きます。皆さんの継続的な支払いを歓迎します。注意!!!
この部分を明確に紹介していない、またはこの部分に問題があると思われる場合は、コメント欄に指摘してください。
この記事が少しでもお役に立てましたら、ワンクリックでつながれば幸いです
皆さんと一緒に進歩していきたいと思っています