[C++ skill tree] Make regular operators used on classes--six member functions of classes II

insert image description here

Halo, this is Ppeua. Usually, I mainly update C language, C++, data structure algorithm... Follow me if you are interested! You will not be disappointed.


insert image description here

0. Operator overloading

In order to enhance the readability of the code, operator overloading is added in C++, just like other function overloading. Its naming format is as follows:

return type operator operator(parameter list)

Date operator<(Date&d1)

But not all operators can be overloaded:

  1. It is not possible to customize a brand new operator, such as @
  2. .* :: sizeof ?: . The above operators cannot be overloaded, especially the first one.*

Next we define a date class as a practice of operator overloading:

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

This is the most basic class function, which includes a default constructor and a copy constructor . The copy constructor will output: "copy" when called, which will help us analyze the function later.

1. Assignment operator = overloaded

Let's take a look at how he defines it. It also conforms to the definition rule above:

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

Why do we need to call by return value? Because it can save space. Parameters are passed by reference, and the return value does not need to call the copy constructor

Isn't this a formal parameter? Why can the scope be referenced? * this itself is a formal parameter, but this points to the date object, and its stack frame is stored in the Main function, so this will be destroyed, and the object pointed to by this will be returned, and will not be destroyed

Adding const is just an insurance, so that we will not change the parameters passed in

We can demonstrate it with a piece of code:

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

First remove both references, and you will find that the copy construction is called twice, which are value passing and return value respectively :

insert image description here

** After adding, the copy construction will not be called, so writing in this way saves space and time. **During the compilation phase, the modified code will be transformed into

d2.operator=(d1)

This is also convenient for us to understand the specific execution method of this operator.

As for why you need to return the Date type? Consider the following scenario:

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

The essence of this code operation is:
insert image description here

So we need to make it return Date type to meet this situation:

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

If the assignment operator is not defined by itself, the system will automatically generate an assignment operator (this is the same as the previously mentioned constructor, copy constructor, and destructor), and its calling rules are the same as before. If there is no definition, the built-in type is completed Shallow copy, the custom type calls its custom assignment operator overload , so it is the same as calling the copy constructor, and the built-in type involving space management, we need to define the copy constructor by ourselves.This also leads to the fact that assignment operators cannot be defined globally, but only in classes, if you define one globally, it will conflict with the one defined in the class.

2. Comparison operator == overloading

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

If all three are the same, it returns true, otherwise it returns false. The overall logic is easy to understand. This function is not a default function, so we can define it globally or define it in a class.

If the definition is global, there will be a problem that private members in the class cannot be accessed (this will be resolved later, but now we put it in the class to avoid this problem)

Then why is this const placed outside? Here is a modification of the this pointer, which is the ontology, because the this pointer exists implicitly, and we will not change the body in this overload, so add a modification to it

The original appearance of the this pointer: the content pointed to by the this pointer cannot be changed, but its value can be changed

Date * const this

Now after modification: neither the content pointed to by this pointer nor its value can be changed, which plays a protective role

const Date * const this 

3. Comparison operator != Overloading

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

The above results are directly reused here, which also proves the power of the overloaded operator hhhh

4. Comparison operator < overloading

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

Mainly for the implementation of logic:

  1. If the year is young, return true directly
  2. If the year is the same and the month is smaller, return true directly
  3. If the year and month are the same and the number of days is small, return true directly

If the above conditions are not met, it means that it is not less than, then return false directly

5. Comparison operator <= overloading

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

It is also a reuse of == and < written before. If it is less than or equal to, it is less than or equal to

6. Comparison Operator > Overloading

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

If not less than and not equal to, less than or equal to

7. Comparison operator >= overloading

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

If not less than, greater than or equal to

8. Assignment operator += and + overloading

In the implementation of the next overload, in order to facilitate obtaining the specific date of each month, including the judgment of leap year and normal year. A getmonth function is implemented in the class to return the specific number of days in each month.

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

Enter the year and month to judge the specific date number, among which the leap year meets the four-year leap, the hundred-year leap, and the four-hundred-year leap

Next, implement +=: Here you need to judge first that if Day<0, it is equivalent to calculating *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;
    }

The specific implementation logic is as follows:
insert image description here

So let's look at the logic of +:

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

In order not to change the Date object itself, you need to make a copy of the Date ( here use the copy constructor, not the assignment operator overload, because it is assigned when the object is initialized ), then reuse +=, and finally return itself. Because it is Local parameters, so references cannot be returned, because tmp will be destroyed when it goes out of scope .

9. Assignment operator -= and - overload:

-=:

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

The overall logic is similar to that of addition, but I will repeat it here.

10. Pre-++ and Post-++

prefix ++

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

If I want to perform ++ operation on a Date object, I can directly: ++Date.

Post ++ : In order to overload again with pre ++, a parameter is added. This parameter will not play any role in the overload process. It is only to distinguish which overload is specific. When using it, the compiler It will pass 0 in the post ++ by itself, we only need to use it normally ,

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

When I was just learning C, I always saw discussions about pre-++ and post-++. Now I can see the overload, because post-++ will copy a copy of the original value, so the efficiency will be lower .

In built-in types, however, this efficiency transformation is negligible .

11. Front – and rear –

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

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

12. Logical operator - overloading

For subtraction between two date classes, calculate the difference between dates

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

Find the largest date first, and approach the largest number of days step by step by combining the smallest ++ and counting days ++, and finally return. Note that passing values ​​​​need to pass references to improve efficiency

13. Stream operator overloading

Why can cout/cin in c++ output/input objects of any built-in type without the object type? Because cout and cin are also a stream object in c++, and the underlying layer implements multiple types of overloading

This overload needs to be defined in the global. If it is defined in the class, according to the conversion rule above, it will become d1<<cout, but if it is defined in the global, there will be cases where private variables cannot be accessed, so we provide a Declaration of a friend function.

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

It can be placed anywhere in the class, indicating that this function is a friend of this class, and can directly access the variables and functions in it (the specific meaning of friends will be explained in detail later,)

13.1 Output stream overloading:

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

ostream is an output stream object, and its return value will be put into the output stream in the output

13.2 Input stream overloading:

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

Overall same as above

14. Complete code:

#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();
}

So far, the class has been completed

15. Address-of operator overloading

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

By default, the compiler will automatically generate this overload, and we don't need to write this overload in most cases.

If you don't want a person to get a modifiable address, but only want him to get an unmodifiable const address, you can write like this

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

16. So far, the six built-in type member functions are completed

img

Guess you like

Origin blog.csdn.net/qq_62839589/article/details/130617601