[C++ Elementary]: Classes and Objects (2)

6 default functions of the class

In the data structure chapter, we use C language to write linked lists, stacks, queues, etc., each of which must be initialized and destroyed. In c++, in order to avoid these repeated operations and other problems, 6 functions are introduced. (the most important are the first four)

insert image description here

1. Constructor

1. Easy to use

Pay attention to the name here, construction is not creating objects, constructors are used for initialization. The destructor is not destroying the object, but to clean up the work.

insert image description here

insert image description here

insert image description here

This function replaces the initialization function. Its function is similar to the initialization function but the difference is that this function will be called automatically without writing codes such as d1.init().

2. Constructor overloading

insert image description here

If we don't want to initialize day, we can write another constructor.

insert image description here


3. Default constructor

insert image description here

The answer is yes, because every class creation will generate a constructor by default, so what is the printed result here?

insert image description here

What's going on here? It seems that the random value compiler does nothing. Actually not.

insert image description here

Next define a type. And debug to see how their values ​​change.

insert image description here
insert image description here
insert image description here

If you don't know much about debugging here, you can take a look at this blog portal . We can see that it is not a random value, but the function automatically initializes the memory to cccccccc when the stack frame is created. For details, please refer to this blog portal . And we can see that our custom type will also be initialized to the same value. (ps: Some compilers will be initialized to 0, but this belongs to the optimization of the compiler, and c++ has no clear regulations, especially for versions above vs2019, it will be very strange here).

insert image description here

insert image description here

insert image description here

insert image description here

Let's look at a question

insert image description here
insert image description here

These are two constructors, which constitute overloading in syntax (if you don't know about overloading, you can read this portal ). However, since there is no need to pass parameters when calling, there will actually be conflicts, so this way of writing is actually not allowed.

insert image description here

Summary: The default constructor can be called without passing parameters.

2. Destructor

insert image description here

insert image description here

This is a stack, the front is a constructor to replace the initialization, and the back is a destructor to replace the original destroy to prevent memory leaks. Both functions are called automatically, so we can directly operate on the stack. .

insert image description here

In this way, you no longer have to worry about forgetting to initialize and destroy the data structure when using it.

3. Copy construction

insert image description here

For example, I want to initialize d2 with the initial value of d1

insert image description here

insert image description here

What should be noted here is why do we use references to pass parameters in the constructor? In fact, this is related to a rule of C++. In C++, if the parameter is a built-in type, then passing by value is a direct copy. If the passed parameter is a custom type, then passing the value is to call the copy construction to complete. So if we don't quote the symbol, then it will keep copying, resulting in an infinite loop.

insert image description here

insert image description here

insert image description here

insert image description here

Next, we will not write the copy structure to see if it can be copied normally.

insert image description here

It seems that the copy function generated by itself can complete the copy, and there is no need to write it yourself. In fact, it is not, because the default copy is simply copied by byte, so some more complex structures cannot be copied. For example, I want to copy a stack below.

typedef int DataType;
class Stack
{
    
    
public:
	Stack(size_t capacity = 10)
	{
    
    
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
    
    
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType & data)
	{
    
    
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
    
    
		if (_array)
		{
    
    
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
    
    
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}

insert image description here

insert image description here

We can see that it is indeed copied, but this will cause the system to crash. In fact, the problem lies in _array. Shallow copy is to copy the value. There is no problem with _size and _capacity, but _array is a pointer. After copying to s2, the two pointers point to the same space. The biggest problem is that when destructing, s1 is destructed once, and s2 is destructed again, causing the same space to be destructed twice, which leads to a system crash.

Therefore, we cannot let two pointers point to the same space, we must write a deep copy by ourselves.

insert image description here

Let’s take a brief look here, and I will explain it in detail later (because there are many scenes)

insert image description here

insert image description here

Four. Assignment operator overloading function

1. Operator overloading

insert image description here

one example

insert image description here

We know that for built-in types (such as int, double...) you can directly use > or <, = to compare, but what if it is a custom type? In c, we can only write a comparison function to judge by ourselves, for example as follows:

insert image description here

insert image description here

But this is not intuitive enough in c++, because if the function name is a popular name like Func, it will be a headache for people who read the code. So is there any way to directly write such obvious code as d1<d2? This depends on operator overloading.

insert image description here

You only need to add an operator and < symbol to the original judgment function to judge whether d1<d2.

insert image description here

There is another problem here, what if I set _year... to private? Private ones cannot be accessed externally, so we need to create functions in the class.

insert image description here

insert image description here

insert image description here

There may be doubts here, there should be two parameters, why only pass one parameter? This is because there is an invisible parameter this pointer, which is also a parameter.

2. Assignment operator overloading function

insert image description here

Note that this is distinguished from copy construction, which uses an existing object to initialize another object. And assignment function overloading is a copy between two objects that already exist.

insert image description here

insert image description here

This seems simple, and indeed it is the simplest case, but there are still some problems here. When assigning a built-in type, we can write a=b=c=4, so can the custom type also be written as d1=d2=d3?

insert image description here

insert image description here

What's going on?

insert image description here

Therefore, in order to be able to assign values ​​​​continuously, we need the return value of d2 to be d2, so that the assignment can be completed successfully.

insert image description here

insert image description here

This is successful but not perfect, because in the previous reference, it was said that copying by value consumes too much ( portal ), so use references as much as possible when you can use references (why can you use references here? Because we return *this, That is, d2, then the return value always exists before d2 is destroyed)

insert image description here

insert image description here

So, in fact, if we don't write the default generated by the compiler here, it can still be compiled and passed. Of course, this is only for objects that are all built-in types and all custom types, such as Date and queue here, but you need to write them yourself if you have both, like the stack.

insert image description here

insert image description here

3. Date addition

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

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

	Date& operator+(int day)
	{
    
    
		_day += day;
		while (_day > GetDay(_year, _month))
		{
    
    
			_day -= GetDay(_year, _month);
			++_month;
			if (_month == 13)
			{
    
    
				_month = 1;
				++_year;
			}
		}
		return *this;
	}
};

int main()
{
    
    
	Date d1(2023,4,29);
	d1 + 3;
	printf("%d %d %d", d1._year, d1._month, d1._day);

	return 0;
}

insert image description here

You can find a problem, that is, we did get the desired result, but we changed the value of d1, so strictly speaking, what we achieve here is actually +=. So next, modify the code so that it only implements addition.

We need the same data as d1 but do not change d1, so we need to perform copy construction.

insert image description here

insert image description here

insert image description here

It should be noted that here tmp is temporarily copied out of scope and will be destroyed, so we cannot use references to avoid garbled characters when returning.

4. Pre ++ and Post ++

Addition is divided into preposition and postposition. In C language, we did not make a distinction, because the efficiency of the two additions is almost the same in the built-in type, but there is a problem in the custom type of C++. The pre-++ is the value after returning ++, and the post-++ is the value before returning ++, so two kinds of ++ need to use two operators to overload the function.

insert image description here

5. << Overload

In C++, there is an operator called stream insertion, which can automatically recognize the type.

insert image description here

How exactly does it work? In fact, it is not mysterious, it is just simple operator overloading. cout actually exists in an ostream class, and this class is in the iostream header file, so we only need to include this header file, and then expand the header file to use cout.

insert image description here

So how does cout automatically identify the type? In fact, it is just using multiple function overloads.

insert image description here

cout can automatically recognize that the built-in type is implemented in the library itself, so we have to overload it ourselves if we want to recognize the custom type.

insert image description here
insert image description here

It is a very simple overload, but it is obviously wrong, why? This is because cout is a binary operator. The first parameter is the left operand, and the second parameter is the right operand. If written here, cout is the first parameter, and d1 is the second parameter. In the member function To use overloading, you must first convert it to d1.oprator<<(cout), which is obviously impossible to pass parameters in this way (in short, Date takes up the first parameter by default). And << does not need a return value, so the return type can be changed to void.

insert image description here

insert image description here

Writing in this way can fulfill our expectations, but it does not conform to our usage habits, so can we write it as cout<<d1? It is not possible, but this can only modify the << in the ostream class in the library. So we can't write << as a member function, because it doesn't conform to our usage habits.

Write << as a global function

insert image description here

In this way, we can freely adjust our parameters when written as a global function, but there is still a problem that if _year, _month... are all private and cannot be accessed directly, there are two ways to solve this problem.

1. Add a function to the class to return data directly (commonly used in Java)

insert image description here

2. Friend function

insert image description here

It is very simple to use, just declare in the class and add a friend in front, so that the function can access the private of the class.

insert image description here

insert image description here

What if I want continuous inflow?

insert image description here

Like the previous continuous assignment, only one return value is required.

insert image description here

insert image description here

insert image description here

6. const members

insert image description here

As an example

I defined a print function in the class and called it with two objects.

insert image description here

insert image description here

Here d1 can be called but d2 can't, why? This is because const is added in front of d2, the parameter passed by d2 is const Date , and the parameter in the Print function is Date , so passing it in the past is a privilege amplification, and naturally it cannot be compiled. So how to solve this problem? **

insert image description here

There is a very simple method, which is to change the parameters in the Print function to const Date*. But we can't change it directly, because this is a hidden parameter and cannot be modified directly. So c++ has introduced a new way to add const outside the brackets.

insert image description here

insert image description here

Pay special attention to the const here to modify *this. So const is only applicable to member functions that will not change (such as +=, -= that need to change the value cannot be used)

Guess you like

Origin blog.csdn.net/m0_73790767/article/details/130313806