C++ classes and objects (5)—stream operator overloading, const, and address taking

Table of contents

1. Stream output

1. Realize a single output

2. Realize continuous output

2. Stream input

  Summarize:

3. const modification

4. Get the address

.Get address and const get address operator overloading

5. [ ] operator overloading


1. Stream output

1. Realize a single output

Create a date class.

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

In the past, if we wanted to output the date in the date class, we needed to create a member function for output in the class.

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

For built-in types, we can directly use cout<< to output their values.

We can also overload stream extraction << to output member values ​​of built-in types. First, let’s understand the origin of cout.

  • iostream is a header file in the C++ standard library that contains the definition of stream classes for input and output. The two base classes  and  are defined in the iostream header file, which are used for input and output operations respectively. istreamostream

  • ostream is a class defined in the iostream header file, which is the base class of the output stream. The ostream class provides basic functions and interfaces for output operations. For example, the << operator is used to output data to the stream.

  • cout is an object of class ostream , which is the standard output stream object. cout objects can use the << operator to output data to the standard output device (usually the console).

iostream is a header file, ostream is the output stream base class defined in iostream , and cout is An object of class a>  operator, we can easily output data to the console.  object and the ostream , used to output data to the standard output device. By using the cout<<

 Now implement the << overloading in the class:

	void operator<<(ostream& out)
	{
		out << _year << "年" << _month << "月" << _day << "日" << endl;
	}

ostream& is a reference type that represents a reference to the output stream object. By using the ostream& reference type, the output stream object can be passed to the operator overload.

 When we want to use operator overloading <<, we need to use the following form:

d1 << cout;//或者d1.operator<<(cout);

The first parameter of operator overloading << is the left operand, and the second parameter is the right operand.

Although this form can output the results we want, it is different from the conventional method we use cout<<d1.

We can modify it and overload the << operator as a global function, taking a reference to the output stream object as the first parameter and a reference to the date class object as the second parameter.

void operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}

At the same time, in order to enable the global << operator overload to access the private member variables of the date class object d, you can create a friend function declaration in the date class, so that you can access the members of the object.

friend void operator<<(ostream& out, const Date& d);

Let’s test it out: 

class Date
{
	friend void operator<<(ostream& out, const Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout<< _year << "年" << _month << "月" << _day << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
void operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}

void Test()
{
	Date a(2023, 11, 24);
	a.Print();
	cout << a;
}

int main()
{
	Test();
	return 0;
}

 Successfully implemented the overloading of operator <<.

 

2. Realize continuous output

What if it is the following continuous output?

void Test2()
{
	Date a(2023, 11, 24);
	Date b(2023, 11, 25);
	cout << a << b << endl;
}

At this time the compiler will report an error.​ 

 

 In order to support the form of continuous output, we need to add a return value to the << overload.

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

At the same time, the friend function declaration also needs to be modified:

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

 have a test:

class Date
{
	friend ostream& operator<<(ostream& out, const Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout<< _year << "年" << _month << "月" << _day << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}
void Test2()
{
	Date a(2023, 11, 24);
	Date b(2023, 11, 25);
	cout << a << b << endl;
}
int main()
{
	Test2();
	return 0;
}

Successfully achieved continuous output: 

2. Stream input

iostream, istream and cin are related classes and objects used for input in C++.

  • iostream is a header file in the C++ standard library that contains the definition of stream classes for input and output. The two base classes  and  are defined in the iostream header file, which are used for input and output operations respectively. istreamostream

  • istream is a class defined in the iostream header file, which is the base class of the input stream. The istream class provides basic functions and interfaces for input operations. For example, the >> operator is used to read data from the stream.

  • cin is an object of class istream , which is the standard input stream object. cin objects can read data from the standard input device (usually the keyboard) using the >> operator.

Summary: iostream is a header file, istream is the input stream base class defined in iostream , and < /span>  operator, we can easily enter data from the keyboard.  object and the  and is used to read data from the standard input device. By using the cin is an object of class istreamcin>>

 Next, we will implement the stream extraction>> operator overloading, which is the same as the stream insertion<< implementation.

istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

 have a test

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

public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout<< _year << "年" << _month << "月" << _day << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
void Test3()
{
	Date d;
	cin >> d;
	cout << d;
}
int main()
{
	Test3();
	return 0;
}

Successfully implemented stream extraction operator overloading.​ 

Summarize:

If the declaration and definition of a class are divided into files, we usually put the stream extraction and stream insertion operator overloads as inline functions in the header file, thus eliminating the linking process and allowing the address to be called during the compilation process.

inline ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

inline istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
  • In C++, if a member function is defined directly inside a class, it is considered an inline function. The definition and declaration of inline functions are in the class definition, so that the compiler can insert the code of the function into the calling location instead of executing it through a function call.
  • Usually, short functions are suitable as inline functions because the cost of calling inline functions is smaller and can avoid the additional overhead of function calls. Defining such a function inside a class can conveniently put its declaration and definition together, improving the readability and maintainability of the code.
  • However, it should be noted that it is ultimately the compiler's decision whether to treat a member function defined within a class as an inline function. The compiler may decide whether to expand a function inline based on factors such as its complexity, frequency of calls, etc.
  • In short, short functions defined inside a class can be regarded as inline functions, which can improve the execution efficiency and readability of the code. But ultimately it is up to the compiler to decide whether to inline expansion.

3. const modification

The const-modified "member function" is called a const member function. The const-modified class member function actually modifies the this pointer implicit in the member function, indicating that no member of the class can be modified in the member function.

Let's look at the following code:

class A {
public:
	void Print() 
    {
		cout << _a << endl;
	}
private:
	int _a = 1;
};
int main()
{
	A aa;
	aa.Print();
	return 0;
}

Successful output: 

Is it okay if aa is modified with const?​ 

const A aa;

The program reports an error after compilation: 

 

This is because of the problem of permission amplification.

 The parameter of the Print function is A*this, and the type of aa is const A*, so aa calling the Print function will cause permission amplification, and if the permissions are translated, it is modified with const in front of the this pointer. This is also prohibited. We cannot modify the this pointer. .

This time there is a new indirect method:

  • The grammar stipulates that it is called a const member function, and const modifies *this, which means that the type of this becomes const A* type.
  • When a member function does not change the member variable internally, it is best to add const. Both const objects and ordinary objects can be called.

 At this time we can implement the implementation ofC++ classes and objects (4)-Date class in the Date.h file of this article Some member functions have been modified with const.

#include <iostream>
#include <assert.h>
using namespace std;

class Date {
public:
	Date(int year = 0, int month = 0, int day = 0);
	void Print();
	int GetMonthDay(int year, int month) 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);

	// d1 - 100
	Date operator-(int day);

	// d1 - d2;
	int operator-(const Date& d) const;

	// ++d1
	Date& operator++();

	// d1++
	Date operator++(int);
	
	Date& operator--();

	Date operator--(int);

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

4. Get the address

.Get address and const get address operator overloading

These two default member functions generally do not need to be redefined, and the compiler will generate them by default.

class Date
{
public :
    Date* operator&()
    {
        return this ; 
    }
    const Date* operator&()const
    {
        return this ;
    }
private :
    int _year ; // 年
    int _month ; // 月
    int _day ; // 日
};
These two operators generally do not need to be overloaded. Just use the default address-taking overloading generated by the compiler. Only in special circumstances, it is necessary.
To overload, for example wants others to get the specified content.

5. [ ] operator overloading

class Array
{
public:
	int& operator[](int i)
	{
		assert(i < 10);

		return _a[i];
	}

	const int& operator[](int i) const
	{
		assert(i < 10);

		return _a[i];
	}
private:
	int _a[10];
	int _size;
};

void Func(const Array& aa)
{
	for (int i = 0; i < 10; ++i)
	{
		//aa[i]++;
		cout << aa[i] << " ";
	}
}

First, let’s look atArrayclasses:

  • ArrayThe class defines a private integer array_a with a size of 10, and a private integer variable_size.

  • ArrayThe class overloads the [] operator so that we can use objects of the Array class just like ordinary arrays.

  • operator[]There are two versions of the function, one is the non-constant version and the other is the constant version. The non-const version returns a modifiable reference, and the constant version returns an unmodifiable constant reference.

  • In the operator[] function, the assert function is used to ensure that the index i is less than 10 to prevent the array from going out of bounds.

Then, let’s look at the Func function:

  • FuncThe function accepts a constant reference of the Array class as a parameter. Because parameters are constant references, we cannot modify the value of the parameter in the function.

  • In theFunc function, there is a loop, and the loop variablei traverses from 0 to 9. In the loop body, aa[i]++ is first commented out. This is because aa is a constant reference and we cannot modify its value. Then, use cout to print out the value of aa[i], and then print a space.

For example, if we create an object of class and initialize the array from 0 to 9, and then call , then will be printed on the console. Arraya_aFunc(a)0 1 2 3 4 5 6 7 8 9

Guess you like

Origin blog.csdn.net/m0_73800602/article/details/134588898