[C++] クラスとオブジェクト (中央) は演算子のオーバーロードの例を完全に説明しています -- Date クラス (Date) -- const メンバー

目次

1 はじめに

2. 完全なデフォルトのコンストラクター

3. 印刷インターフェイス

4. コピー構築

5. 代入演算子のオーバーロード (operator=)

5.1 代入オーバーロードはデフォルトのメンバー関数、オーバーロード形式です。

5.2 代入のオーバーロードをグローバル関数にすることはできません

5.3 コンパイラがデフォルトで生成する

6. デストラクター

7、オペレーター>

8、演算子==

9、演算子>=

10、演算子<

11、演算子<=

12、オペレーター!=

13. 演算子+= (日付 += 日)

14. 演算子+ (日付 + 日数)

15. 演算子-= (日付-=日)

16. 演算子- (日付-日)

17. ++前、++後、--前、--後

18. Date - 日付 (返される日数)

19. const メンバー


1 はじめに

この記事では主に次のインターフェイスを実装します。

#include <iostream>
using namespace std;

class Date
{
public:

	// 获取某年某月的天数
	int GetMonthDay(int year, int month) const;
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	//打印接口
	void Print() const;


	// 拷贝构造函数
	// d2(d1)
	Date(const Date& d);
	// 赋值运算符重载
	// d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d);
	// 析构函数
	~Date();


	//总结一下:只读函数可以加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;
	// !=运算符重载
	bool operator!=(const Date& d) const;

	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day) const;
	// 日期-天数
	Date operator-(int day) const;
	// 日期-=天数
	Date& operator-=(int day);


	// 函数重载
	// 运算符重载
	// 前置++
	Date& operator++(); //++d1 -> d1.operator()
	// 加一个int参数,进行占位,跟前置++构成函数重载进行区分
	// 后置++
	Date operator++(int); //d1++ -> d1.operator(0)
	// 后置--
	Date operator--(int);
	// 前置--
	Date& operator--();


	// 日期-日期 返回天数
	int operator-(const Date& d) const;

private:

	int _year;
	int _month;
	int _day;

};

このプロジェクトは、次の 3 つのファイルを含む複数のファイルに記述します。

次に、上記のインターフェイスを 1 つずつ実装します。

2. 完全なデフォルトのコンストラクター

フルデフォルトのコンストラクタを普通に書くと欠点があり、インスタンス化されたオブジェクトは初期化されるものの、引数として渡された月日が存在しない場合は不正となるため判断が必要ですが、ここで月の日数を表す関数を作成し、作成時に比較します。

ここでは、まず GetMonthDay インターフェイスを実装します。

// 获取某年某月的天数
int Date::GetMonthDay(int year, int month) const
{
	static int monthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

	if(2 == month
		&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		return 29;
	}

	return monthDay[month];
}
// 全缺省的构造函数
Date::Date(int year, int month, int day)
{
	if (month < 1 || month > 12
		|| day < 1 || day > GetMonthDay(year, month))// 判断日期是否合法
	{
		cout << "非法日期" << endl;
		exit(-1);
	}
	else
	{
		_year = year;
		_month = month;
		_day = day;
	}
}

GetMonthDay インターフェイスでは、各月の日数を格納する配列を作成しました。デフォルトの 2 月は 28 日です。以下では、2 月の日数を見つけるかどうかを判断し、年を判断して、2 月の日数を確認します。は閏年です。閏年の場合は 29 を直接返し、それ以外の場合は月に対応する日数を直接返します。この関数は後で継続的に呼び出されるため、配列に静的な変更を使用します。そのため、静的領域に配置し、一度だけ開くことで時間を節約し、一度にかかる時間はそれほど長くありません。通話数が多い場合、効率が大幅に向上します。

ここで、GetMonthDay インターフェイスの後に const を追加する理由を尋ねられます。ここで説明します。

3. 印刷インターフェイス

印刷インターフェイスについては何も説明する必要がないので、直接説明しましょう。

//打印接口
void Date::Print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

印刷インターフェイスは、渡すオブジェクトが const によって変更される可能性があるため、このために const 変更を使用します。印刷インターフェイスが const を追加しない場合、アクセス許可の拡大の問題が発生し、エラーが発生します。constに変更後は権限に問題はありません。

4. コピー構築

コピーについてまだよくわからない場合は、コピー構造の詳細な説明が記載されている以下のリンクをクリックしてください。ここをクリックしてください。

// 拷贝构造函数
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

5. 代入演算子のオーバーロード (operator=)

5.1 代入オーバーロードはデフォルトのメンバー関数、オーバーロード形式です。

パラメータの型: const T&、参照を渡すとパラメータの受け渡しの効率が向上します
戻り値の型: T&、参照を返すと返す効率が向上します、戻り値の目的は、それ自体に代入するかどうかを検出するための連続代入をサポートすることです return
*
this : 連続代入の意味を複合化します。

オーバーロードされた形式で書いてみましょう。

// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& Date::operator=(const Date& d)
{
	if (this != &d)// 存在this就是d的情况
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	return *this;
}

5.2 代入のオーバーロードをグローバル関数にすることはできません

理由: 代入演算子が明示的に実装されていない場合、コンパイラはデフォルトの代入演算子を生成します。このとき、ユーザーがグローバル代入演算子オーバーロードをクラス外で実装すると、クラス内のコンパイラによって生成されるデフォルトの代入演算子オーバーロードと競合するため、代入演算子オーバーロードはクラスのメンバー関数のみにすることができます。

自分で書いてみましょう:

ここで使用する vs2019 コンパイラでは、コンパイラはコンパイル中にエラーが直接報告されることを通知するため、代入演算子のオーバーロードをグローバル関数にすることはできません。

5.3 コンパイラがデフォルトで生成する

これを記述しない場合、コンパイラはコピー オーバーロード関数を自動的に生成しますが、デフォルトで生成される関数は組み込み型に直接割り当てられ、カスタム型のメンバー変数は対応するクラス割り当てオーバーロードを呼び出す必要があります。割り当てを完了する関数。

したがって、メンバー変数にカスタム型 (クラス型) が存在する場合、そのカスタム型の代入オーバーロード関数は正しい必要があります。これは正しいです。

2 つのスタックがキューを実装している場合、キューの割り当てオーバーロード関数はデフォルトで生成できますが、スタックにはアプリケーション リソースがあるため、スタックは独自に書き込む必要があります。直接コピーすると、2 つのスタックは同じリソースを使用します。この場合、1 つはポップアウトされるスタックと、スタックにプッシュされるスタックです。スタック内にあるかどうかに関係なく、実際には同じスタック上で動作します。これは間違っています。

注:クラスにリソース管理が関与していない場合、代入演算子を実装できるかどうかに関係なく、リソース管理が関与した場合は実装する必要があります。

代入とコピーの違い:コピーは、作成される別のオブジェクトを初期化するための既存のオブジェクトです。

割り当てとは、2 つの既存のオブジェクトをコピーすることです。

6. デストラクター

デストラクターについてまだよくわからない場合は、デストラクターの詳細な説明が記載されている次のリンクをクリックしてください。ここをクリックしてください。

// 析构函数
Date::~Date()
{
	_year = 0;
	_month = 0;
	_day = 0;
}

7、オペレーター>

日付>日付で判断

1. その年未満の場合は比較する必要はなく、直接 false を返します。

2. 同じ年の同じ月を比較し、月がそれより小さい場合は、直接 false を返します

3. 年と月が同じで、日がそれより小さい場合は false を返します

4. 上記の判定が正しくない場合は、前の日付が後の日付より大きいことを意味し、true を返します。

// >运算符重载
bool Date::operator>(const Date& d) const
{
	if (_year < d._year)
		return false;
	else if (_year == d._year && _month < d._month)
		return false;
	else if (_year == d._year && _month == d._month && _day < d._day)
		return false;
	else
		return true;
}

8、演算子==

date == date の判断は非常に簡単です。年、月、日が等しいのは正しいです。コードの実装を見てみましょう。

// ==运算符重载
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
{
	return _year == d._year 
		&& _month == d._month 
		&& _day == d._day;
}

9、演算子>=

>= については、実際に判断するためのロジックを作成することなく、上記の > と == を再利用できます。

実装コードを見てみましょう。

// >=运算符重载
bool Date::operator>=(const Date& d) const
{
	return (*this > d) || (*this == d);
}

10、演算子<

< と >= は逆の論理なので、 >= を否定することで実現できます。

// <运算符重载
bool Date::operator<(const Date& d) const
{
	return !(*this >= d);
}

11、演算子<=

<= と > は逆の論理なので、> を否定することで実現できます。

// <=运算符重载
bool Date::operator<=(const Date& d) const
{
	return !(*this > d);
}

12、オペレーター!=

!= と == は逆の論理なので、== を否定することで実現できます。

// !=运算符重载
bool Date::operator!=(const Date& d) const
{
	return !(*this == d);
}

13. 演算子+= (日付 += 日)

+= は元のベースで変更されるため、暗黙の this を const で変更することはできません。元のベースで変更されるため、 += 関数の本体が表示され、オブジェクトはまだそこにあり、参照によってそれを返します。コピーを減らすことができます。

+= 日に対して分析をプロットします

コード:

// 日期+=天数
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= (-day);
	}

	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		//月进位
		_month++;

		//月满了
		if (13 == _month)
		{
			_year++;
			_month = 1;
		}
	}

	return *this;
}

テストしてみましょう:

書いた += が正しいか確認してください

14. 演算子+ (日付 + 日数)

+ と += の違いは、+= はオブジェクト自体に対する + であり、+ はオブジェクト自体に対する変更ではないことです。したがって、新しいオブジェクトをインスタンス化し、渡されたオブジェクトを新しいオブジェクトにコピーし、+ 日間の結果を保存して返す必要があります。

// 日期+天数
Date Date::operator+(int day) const
{
	Date tmp(*this);

	tmp += day;

	return tmp;
}

ここでは、+= のコードを 1 つコピーした後、再利用できます。

渡されたオブジェクトと + の後のオブジェクトの結果を見てみましょう。

ここで、+ は d1 の結果に影響を与えないことがわかります。

15. 演算子-= (日付-=日)

-= は元のベースで変更されるため、暗黙の this を const で変更することはできません。元のベースで変更されるため、 -= 関数の本体が表示され、オブジェクトはまだそこにあります。参照によって返します。コピーを減らすことができます。

-= 日数を分析するグラフを描きます

コード:

// 日期-=天数
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += (-day);
	}

	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (0 == _month)
		{
			_year--;
			_month = 12;
		}

		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

操作結果:

比較して、正しく実装されているかどうかを確認します。

16. 演算子- (日付-日)

- と + のロジックは似ていますが、- と -= の違いは、-= はオブジェクト自体に対する - であり、- はオブジェクト自体に対する変更ではないことです。したがって、新しいオブジェクトをインスタンス化し、渡されたオブジェクトを新しいオブジェクトにコピーし、-日分の結果を保存して返す必要があります。

// 日期-天数
Date Date::operator-(int day) const
{
	Date tmp(*this);

	tmp -= day;

	return tmp;
}

これをここにコピーした後、コピーした tmp で -= を再利用できます。

テストしてみましょう:

17. ++前、++後、--前、--後

pre-++とpost-++、pre--とpost--では、関数名は同じですが、実装されている機能が異なります。シンボルテーブルで探す場合、どうすれば明確に区別できるのでしょうか??

この種の問題に対して、C++ には独自のルールがあります。C ++ では、後置位置 ++ がオーバーロードされるときに int 型の追加パラメーターが追加されると規定していますが、関数の呼び出し時にこのパラメーターを渡す必要はなく、コンパイラはそれを自動的に渡します。リアも同様です。

このようにして、シンボルテーブル内の関数名は同じでも、パラメータを持つ関数とパラメータを持たない関数のオーバーロードが実現できます。

これらの関数インターフェイスを個別に実装しましょう。

pre-++ のルールは、まず ++、次に使用です。したがって、pre-++ は +=1 に相当し、多重化 += に相当します。

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

++ 後のルールは、最初に使用し、次に ++ を使用することです。ここでは、まず this のコピーを作成し、コピーしたオブジェクトを this+=1 に返します。これにより、最初に this を使用し、次に ++ を使用できるようになります。

// 后置++
Date Date::operator++(int) //d1++ -> d1.operator(0)
{
	Date tmp(*this);

	*this += 1;

	return tmp;
}

pre--- のルールは、最初に --、次に使用です。したがって、前置詞 ++ は -=1 および多重化 -= と同等です。

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

	return *this;
}

post-- ルールは、最初に使用し、次に -- を使用します。ここでは、まず this のコピーを作成し、コピーしたオブジェクトを this-=1 に返します。これにより、最初に this を使用し、次に -- を使用できるようになります。

// 后置--
Date Date::operator--(int)
{
	Date tmp(*this);

	*this -= 1;

	return tmp;
}

18. Date - 日付 (返される日数)

このインターフェイスは日付-日付です。ここで細かい点を説明します。小さい日付-大きい日付の場合、戻り値は負の数値になるため、この詳細に注意する必要があります。このインターフェイスのアイデアを整理しましょう。

1. 仮説メソッドを使用し、これが大きな日付であると仮定して max オブジェクトに割り当て、2 番目のパラメーターが小さな日付であると仮定して min オブジェクトに割り当て、1 に初期化するフラグを定義します。

2. 比較し、これが小さい日付である場合は、max オブジェクトと min オブジェクトの内容を再割り当てし、フラグを -1 に割り当てます。

3. カウンタ n を定義し、小が大を追い、++min 1 回、カウンタも ++、追いついた後にループから抜け出し、n*flag を返します。

注: 小さなものを大きく追いかける場合は、pre-++ を使用します。pre-++ と post-++ の両方を実現できますが、post-++ のインターフェイスは 2 回コピーし、最初に 1 回コピーする必要があります。 、戻り値は再びコピーされます。一度コピーすると、pre-++ により、大量の計算を計算するときに時間とスペースの 2 つの利点を実現できます。

コードを実装しましょう:

// 日期-日期 返回天数
int Date::operator-(const Date& d) const
{
	int flag = 1;
	Date max = *this;
	Date min = d;
	if (max < min)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	int n = 0;
	while (min != max)
	{
		++n;
		++min;
	}

	return n * flag;
}

テスト:

19. const メンバー

const 修飾された「メンバー関数」は const メンバー関数と呼ばれます。const 修飾されたクラス メンバー関数は、実際にはメンバー関数の暗黙的な this ポインターを変更し、クラスのメンバーがメンバー関数内で変更できないことを示します。

ここでは印刷インターフェイスを使用します。

const で変更した後は、オーバーロードを構成するため、元のメンバー関数と競合しません。

印刷インターフェイスの 2 つのバージョンを見てみましょう。

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

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

print 関数の場合、関数内のメンバーは変更されず、読み取り専用関数です。

したがって、const 装飾を使用しますが、オブジェクトが const 装飾の場合、Print 関数が const 装飾を使用しない場合、呼び出し時に権限増幅の問題が発生します。

まず、const で変更された Print 関数を閉じましょう。

const で変更されたインターフェイスを見てみましょう。

概要:同じ関数について、const 変更された this 関数と装飾されていない関数はオーバーロードを構成します。

メンバーの変更を伴わない関数は読み取り専用関数であり、const 変更後はより安全であり、定数プロパティを持つオブジェクトと互換性があります。

おすすめ

転載: blog.csdn.net/Ljy_cx_21_4_3/article/details/132128446