[C++ スキルツリー] クラスで使用する通常の演算子を作成する - クラスの 6 つのメンバー関数 II

ここに画像の説明を挿入

こんにちは、プペウアです。普段はC言語、C++、データ構造アルゴリズムを中心に更新しています...興味のある方はフォローしてください!がっかりしませんよ。


ここに画像の説明を挿入

0. 演算子のオーバーロード

コードの可読性を高めるために、他の関数のオーバーロードと同様に、演算子のオーバーロードが C++ に追加されます。その命名形式は次のとおりです。

戻り値の型 演算子 演算子(パラメータリスト)

Date operator<(Date&d1)

ただし、すべての演算子をオーバーロードできるわけではありません。

  1. @ などの新しい演算子をカスタマイズすることはできません。
  2. .* :: sizeof ?: .上記の演算子、特に最初の演算子はオーバーロードできません。.*

次に、演算子のオーバーロードの実践として日付クラスを定義します。

class Date{
    
    
public:
    Date(int day=1,int month=1,int year=1)
    {
    
    
        if(month>0&&month<13&&day>0&&day<getmonth(year,month))
            _day=day,_month=month,_year=year;
        else
            cout<<"输入错误";
    }

    Date(const Date&d)
    {
    
    
        _day=d._day;
        _month=d._month;
        _year=d._year;
        cout<<"copy";
    }
private:
    int _year;
    int _month;
    int _day;
}

これは最も基本的なクラス関数であり、デフォルト コンストラクターとコピー コンストラクターが含まれています。コピー コンストラクターは呼び出されると「copy」を出力します。これは、後で関数を分析するのに役立ちます。

1. 代入演算子 = オーバーロード

彼がそれをどのように定義しているかを見てみましょう。これは上記の定義規則にも準拠しています。

 Date& operator=(const Date&d)
    {
    
    
        _day=d._day;
        _month=d._month;
        _year=d._year;
        return *this;
    }

なぜ戻り値で呼び出す必要があるのでしょうか?それはスペースを節約できるからです。パラメーターは参照によって渡され、戻り値はコピー コンストラクターを呼び出す必要はありません。

これは仮パラメータではないでしょうか? なぜスコープを参照できるのでしょうか? *これ自体は仮パラメータですが、これは日付オブジェクトを指しており、そのスタック フレームは Main 関数に格納されているため、これは破棄され、this が指すオブジェクトは返され、破棄されません

const の追加は、渡されるパラメータを変更しないための単なる保険です。

コードでそれを実証できます。

class Date{
    
    
    
public:
    
   
    Date(int day=1,int month=1,int year=1)
    {
    
    
        if(month>0&&month<13&&day>0&&day<getmonth(year,month))
            _day=day,_month=month,_year=year;
        else
            cout<<"输入错误";
    }

    Date(const Date &d)
    {
    
    
        _day=d._day;
        _month=d._month;
        _year=d._year;
        cout<<"copy";
    }

    Date& operator=(const Date &d)
    {
    
    
        _day=d._day;
        _month=d._month;
        _year=d._year;
        return *this;
    }
int main()
{
    
     
    Date d1(2,5,2024);
    Date d2;
    d2=d1;
}

まず両方の参照を削除すると、コピー構築が 2 回呼び出されることがわかります。それぞれ値の受け渡しと戻り値です

ここに画像の説明を挿入

** 追加後はコピー構築が呼び出されないため、このように記述するとスペースと時間を節約できます。**コンパイル段階で、変更されたコードは次のように変換されます。

d2.operator=(d1)

これは、この演算子の具体的な実行方法を理解するのにも便利です。

なぜ Date 型を返す必要があるのでしょうか? 次のシナリオを考えてみましょう。

int a;
int b;
int c;
a=b=c=1;

このコード操作の本質は次のとおりです。
ここに画像の説明を挿入

したがって、この状況に合わせて Date 型を返すようにする必要があります。

int main()
{
    
    
     
    Date d1(2,5,2024);
    Date d2;
    Date d3;
    d3=d2=d1;
}

代入演算子が独自に定義されていない場合は、システムが自動的に代入演算子を生成し(これは前述のコンストラクター、コピー コンストラクター、デストラクターと同じです)、その呼び出し規則は以前と同じです。定義、組み込み型が完了しました 浅いコピー、カスタム型はカスタム代入演算子のオーバーロードを呼び出します。したがって、コピー コンストラクターを呼び出すのと同じであり、スペース管理を伴う組み込み型では、コピー コンストラクターを定義する必要があります。自分たちで。これは、代入演算子がグローバルに定義できず、クラス内でのみ定義できるという事実にもつながります。、グローバルに定義すると、クラスで定義されたものと競合します。

2. 比較演算子 == オーバーロード

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

3 つすべてが同じ場合は true を返し、そうでない場合は false を返します。全体のロジックは理解しやすいです。この関数はデフォルト関数ではないため、グローバルに定義することも、クラス内で定義することもできます。

定義がグローバルの場合、クラス内のプライベートメンバーにアクセスできないという問題が発生します(これは後で解決されますが、この問題を回避するためにクラス内に配置します)。

では、なぜこの const が外部に配置されるのでしょうか。これはオントロジである this ポインターの変更です。 this ポインターは暗黙的に存在し、このオーバーロードの本体は変更しないため、それに変更を加えます。

this ポインターの元の外観: this ポインターが指す内容は変更できませんが、その値は変更できます。

Date * const this

変更後:このポインタが指す内容もその値も変更できません。これは保護的な役割を果たします。

const Date * const this 

3. 比較演算子 != オーバーロード

 bool operator!=(const Date&d)const
    {
    
    
        if(*this==d)return false;
        return true;
    }

上記の結果はここで直接再利用されており、オーバーロードされた演算子 hhh の威力も証明されています。

4. 比較演算子 < オーバーロード

 bool 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;  
    }

主にロジックの実装用:

  1. 年が若い場合は直接 true を返します
  2. 年が同じで月の方が小さい場合は、直接 true を返します
  3. 年と月が同じで日数が少ない場合は直接trueを返す

上記の条件が満たされない場合は、それ以上であることを意味し、直接 false を返します

5. 比較演算子 <= オーバーロード

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

これも前に書いた == と < の再利用です。以下の場合は以下です。

6. 比較演算子 > オーバーロード

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

以下ではなく、以下の場合は、以下となります。

7. 比較演算子 >= オーバーロード

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

未満でない場合は、以上

8. 代入演算子 += および + のオーバーロード

次のオーバーロードの実装において、閏年と平年の判断を含む各月の特定の日付の取得を容易にするため。getmonth関数がクラスに実装されており、各月の特定の日数を返します。

int getmonth(int year,int month)
    {
    
    
        static int monthday[13]={
    
    0,31,28,31,30,31,30,31,31,30,31,30,31};
        if((year%4==0&&year%100!=0)||year%400==0)
        {
    
    
            if(month==2)return 29;
        }
        return monthday[month];
    }

年と月を入力して、閏年が四年閏、百年閏、四百年閏にあたる特定の日付番号を判定します。

次に、+= を実装します。ここでは、Day<0 の場合、*this-=Day を計算するのと同等であることを最初に判断する必要があります。

Date& operator+=(int Day)
    {
    
    
    	if(Day<0)
        {
    
    
            *this-=(-Day);
            return *this;
        }
        _day+=Day;
        while(_day>getmonth(_year,_month))
        {
    
    
            _day-=getmonth(_year,_month);
            ++_month;
            if(_month>12)
            {
    
    
                _month=1;
                _year++;
            }
        }
        return *this;
    }

具体的な実装ロジックは次のとおりです。
ここに画像の説明を挿入

+ のロジックを見てみましょう。

Date operator+(int day)
    {
    
    
        Date tmp=*this;
        tmp+=day;
        tmp.print();
        return tmp;
    }

Date オブジェクト自体を変更しないようにするには、Date のコピーを作成する必要があります (ここでは、オブジェクトの初期化時に割り当てられるため、代入演算子オーバーロードではなくコピー コンストラクターを使用します)。次に += を再利用し、最後にLocal パラメータなので参照を返すことはできません、スコープ外に出ると tmp が破棄されるためです

9. 代入演算子 -= および - のオーバーロード:

-= :

 Date& operator-=(const int Day)
    {
    
    
        if(Day<0)
        {
    
    
            *this+=(-Day);
            return *this;
        }
        _day-=Day;
        while(_day<=0)
        {
    
    
            --_month;
            if(_month<1)
            {
    
    
                _year--;
                _month=12;
            }
            _day+=getmonth(_year,_month);
        }
        return *this;
    }

-:

Date operator-(const int Day)
    {
    
    
        Date tmp=*this;
        return tmp-=Day;
    }

全体的なロジックは加算のロジックと似ていますが、ここで繰り返します。

10. プレ++とポスト++

プレフィックス++

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

Date オブジェクトに対して ++ 操作を実行したい場合は、直接 ++Date を実行できます。

Post ++ : pre ++ で再度オーバーロードするために、パラメータが追加されます。このパラメータは、オーバーロード プロセスでは何の役割も果たしません。どのオーバーロードが特定であるかを区別するためだけです。これを使用する場合、コンパイラに渡されます。 post ++ の 0 自体は、通常どおり使用するだけで済みます

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

私が C を学習したばかりの頃、pre-++ と post-++ についての議論をよく目にしていましたが、post-++ は元の値のコピーをコピーするため、効率が低下するため、オーバーロードが理解できるようになりました。

ただし、組み込み型では、この効率の変化は無視できます

11. フロントとリア

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

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

12. 論理演算子 - オーバーロード

2 つの日付クラス間の減算では、日付間の差を計算します。

int operator-(Date &d)
    {
    
    
        Date max=*this;
        Date min=d;
        int flag=1;
        if(max<min)
        {
    
    
            max=d;
            min=*this;
            flag=-1;
        }
        int n=0;
        while(min!=max)
        {
    
    
            ++min;
            ++n;
        }
        return n*flag;
    }

最初に最大の日付を見つけ、最小の ++ と日数のカウント ++ を組み合わせて段階的に最大の日数に近づき、最後に戻ります。効率を向上させるために、値を渡すには参照を渡す必要があることに注意してください

13. ストリーム演算子のオーバーロード

C++ の cout/cin は、オブジェクト型を持たずに任意の組み込み型のオブジェクトを出力/入力できるのはなぜですか? cout と cin は C++ のストリーム オブジェクトでもあり、基礎となる層が複数の種類のオーバーロードを実装しているためです。

このオーバーロードはグローバルに定義する必要があり、クラスに定義すると上記の変換ルールにより d1<<cout となりますが、グローバルに定義するとプライベート変数が定義できない場合があります。アクセスされる可能性があるため、フレンド宣言機能を提供します。

  friend ostream& operator<<(ostream& out,const Date&d);
  friend istream& operator>>(istream& in, Date&d);

これはクラス内のどこにでも配置でき、この関数がこのクラスのフレンドであることを示し、クラス内の変数や関数に直接アクセスできます(フレンドの具体的な意味については後で詳しく説明します)。

13.1 出力ストリームのオーバーロード:

ostream& operator<<(ostream& out,const Date&d)
{
    
    
   out<<"day: "<<d._day<<" month: "<<d._month<<" year: "<<d._year<<endl;
   return out;
}

ostream は出力ストリーム オブジェクトであり、その戻り値は出力の出力ストリームに入れられます。

13.2 入力ストリームのオーバーロード:

istream& operator>>(istream& in,Date&d)
{
    
    
   	int year, month, day;
	in >> day >> month >>year;

	if (month > 0 && month < 13
		&& day > 0 && day <= d.getmonth(year, month))
	{
    
    
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
    
    
		cout << "非法日期" << endl;
	}
	return in;
}

全体的には上記と同じ

14. 完全なコード:

#include<iostream>
using namespace std;
class Date{
    
    
    
public:
    
    friend ostream& operator<<(ostream& out,const Date&d);
    friend istream& operator>>(istream& in, Date&d);
   
    Date(int day=1,int month=1,int year=1)
    {
    
    
        if(month>0&&month<13&&day>0&&day<getmonth(year,month))
            _day=day,_month=month,_year=year;
        else
            cout<<"输入错误";
    }

    Date(const Date&d)
    {
    
    
        _day=d._day;
        _month=d._month;
        _year=d._year;
        cout<<"copy"<<endl;
    }

    int getmonth(int year,int month)
    {
    
    
        static int monthday[13]={
    
    0,31,28,31,30,31,30,31,31,30,31,30,31};
        if((year%4==0&&year%100!=0)||year%400==0)
        {
    
    
            if(month==2)return 29;
        }
        return monthday[month];
    }

    void print()
    {
    
    
        cout<<"day: "<<_day<<" month: "<<_month<<" year: "<<_year<<endl;
    }


    Date operator=(const Date d)
    {
    
    
        _day=d._day;
        _month=d._month;
        _year=d._year;
        return *this;
    }


    Date& operator+=(int Day)
    {
    
    
        if(Day<0)
        {
    
    
            *this-=(-Day);
            return *this;
        }
        _day+=Day;
        while(_day>getmonth(_year,_month))
        {
    
    
            _day-=getmonth(_year,_month);
            ++_month;
            if(_month>12)
            {
    
    
                _month=1;
                _year++;
            }
        }
        return *this;
    }
    Date operator+(int day)
    {
    
    
        Date tmp=*this;
        tmp+=day;
        tmp.print();
        return tmp;
    }
    Date& operator++()
    {
    
    
        *this+=1;
        return *this;
    }
    Date operator++(int)
    {
    
    
        Date tmp=*this;
        *this+=1;
        return tmp;
    }

    int operator-(Date &d)
    {
    
    
        Date max=*this;
        Date min=d;
        int flag=1;
        if(max<min)
        {
    
    
            max=d;
            min=*this;
            flag=-1;
        }
        int n=0;
        while(min!=max)
        {
    
    
            ++min;
            ++n;
        }
        return n*flag;
    }
    
    Date& operator-=(const int Day)
    {
    
    
        if(Day<0)
        {
    
    
            *this+=(-Day);
            return *this;
        }
        _day-=Day;
        while(_day<=0)
        {
    
    
            --_month;
            if(_month<1)
            {
    
    
                _year--;
                _month=12;
            }
            _day+=getmonth(_year,_month);
        }
        return *this;
    }
    
    Date operator-(const int Day)
    {
    
    
        Date tmp=*this;
        return tmp-=Day;
    }

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

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



    bool operator==(const Date&d)const
    {
    
    
        if(_year==d._year&&_month==d._month&&_day==d._day)
        {
    
    
            return true;
        }
        return false;
    }
    bool operator!=(const Date&d)const
    {
    
    
        if(*this==d)return false;
        return true;
    }
    bool 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 operator<=(const Date &d)const
    {
    
    
        return *this<d||*this==d;
    }

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


private:
    int _year;
    int _month;
    int _day;
};

ostream& operator<<(ostream& out,const Date&d)
{
    
    
   out<<"day: "<<d._day<<" month: "<<d._month<<" year: "<<d._year<<endl;
   return out;
}

istream& operator>>(istream& in,Date&d)
{
    
    
   	int year, month, day;
	in >> day >> month >>year;

	if (month > 0 && month < 13
		&& day > 0 && day <= d.getmonth(year, month))
	{
    
    
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
    
    
		cout << "非法日期" << endl;
	}
	return in;
}

int main()
{
    
    
     
    Date d1(2,5,2024);
    d1+=100;
    d1.print();
}

ここまでで授業は完了しました

15. 演算子のアドレスのオーバーロード

class Date
{
    
    
public :
	Date* operator&()
	{
    
    
		return this ;
	}
	const Date* operator&()const
    {
    
    
    	return this ;
    }
private :
    int _year ; 
    int _month ; 
    int _day ; 
};  

デフォルトでは、コンパイラはこのオーバーロードを自動的に生成するため、ほとんどの場合、このオーバーロードを記述する必要はありません。

変更可能なアドレスを取得したくないが、変更不可能な const アドレスだけを取得したい場合は、次のように記述できます。

class Date
{
    
    
public :
	Date* operator&()
	{
    
    
		return NULL ;
	}
	const Date* operator&()const
    {
    
    
    	return this ;
    }
private :
    int _year ; 
    int _month ; 
    int _day ; 
};  

16. ここまでで 6 つの組み込み型メンバー関数が完成しました

画像

おすすめ

転載: blog.csdn.net/qq_62839589/article/details/130617601