C++: classes and objects (in) --- default member functions --- operator overloading --- the meaning of const

default member function

First of all, we need to understand what is the default member function: when the class is not written, the compiler will generate six default member functions. The
user does not explicitly implement it, but the member function generated by the compiler is the default member function

insert image description here
Let's introduce these functions one by one

Constructor

In C language, whether it is to implement various data structures such as stack queue linked list, it is inevitable to write the Init initialization function. The function of this function is to give a variable an initialized value. In C++, I think these problems of C language are a little bit Trouble, so a certain optimization was carried out. The constructor is to set the information into it when the object is created.

The constructor is a special member function with the same name as the class name. It is automatically called by the compiler when creating a type object to ensure that each data member in the class has its own initial value. Called only once in the lifetime

Constructor Features

A constructor is a special member function whose main function is to initialize an object

It has the following characteristics


  1. The function name is the same as the class name
  2. no return value
  3. The corresponding constructor is automatically called when the object is instantiated.
  4. Constructors can be overloaded
  5. If there is no explicit constructor in the class, a default constructor with no parameters will be automatically generated. If the user defines a constructor, it will no longer be generated.
  6. The default constructor will be generated when it is not written, and the members of the built-in type will not be processed (the declaration in C++11 supports default values), and the members of the custom type will be processed, and this member will be called back default constructor for
  7. The no-argument constructor and the all-default constructor are both default constructors, and only one of these two constructors can exist

Since it has so many characteristics, let's demonstrate its characteristics one by one.

1. The function name is the same as the class name
2. There is no return value
3. The corresponding constructor will be called automatically when the object is instantiated
4. The constructor can be overloaded

insert image description here
When actually writing code, try to write all default constructors, so that no matter how you give parameters, there are certain initialization values

class Date
{
    
    
public:
	Date(int year=1, int month=1, int day=1)
	{
    
    
		cout << "Date(int year, int month, int day)" << endl;
		_year = year;
		_month = month;
		_day = day;
	}

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

int main()
{
    
    
	Date d1;
	Date d2(2004);
	Date d3(2004, 6);
	Date d4(2004,6,16);
	return 0;
}

insert image description here

5. If there is no explicit definition of a constructor in the class, the compiler will automatically generate a default constructor without parameters , and it will no longer be generated once the user has an explicit definition

This is also the first default constructor we have come into contact with, we use the following code to prove its existence

insert image description here
Let's continue to verify its existence in the second half

insert image description here
It can be seen from this that when we do not define a constructor, the compiler will execute a default constructor to give the initial value of the data member when we define a parameterless object. Now that we define a constructor, the default constructor no longer exists. At this time The compiler cannot execute the default constructor to initialize the value of d1 without parameters. At this time, an error will be reported—Date does not have a default constructor, and there is no suitable constructor to use

6. The default constructor will be generated when it is not written. The members of the built-in type will not be processed (the declaration in C++11 supports default values), and the members of the custom type will be processed. Go back and call this member's default constructor

insert image description here
insert image description here
To put it simply, in general, we need to write the constructor by ourselves to determine the initialization method. The member variables are all custom types, so we can consider not writing the constructor, because the constructor of the custom type will be called

7. The no-argument constructor and the all-default constructor are both default constructors, and only one of these two constructors can exist

insert image description here

There will be an initialization list later, which we will explain later

destructor

The destructor is relatively simple

What is the meaning of its existence?
When we implement data structures such as stack sequence list and linked list, we must open up a space in malloc on the heap, but do we often forget to free? This can cause a dangerous situation of memory leaks

Then C++ thought of this problem when it was developed, so the destructor was created in this way. The meaning of its existence is to complete the cleaning of resources in the object, and it will be called automatically before the object is destroyed.

The characteristics of the destructor are as follows

  1. The destructor name is prefixed with the character ~ before the class name.
  2. No parameters and no return type.
  3. A class can have only one destructor. If not explicitly defined, the system will automatically generate a default destructor. Destructors cannot be overloaded
  4. When the life cycle of the object ends, the C++ compiler automatically calls the destructor.
  5. The call order satisfies the order of the stack

insert image description here

copy constructor

In actual code application, we often copy an object to do other things. In C language, this process is very simple, just copy all the values ​​from one content to another directly, but in C++ it is not so easy

Introduction of copy constructor

First of all, it must be clear that after the heap is created, it will not be restored unless it is passed free, so if there is such a C language code:

void push(Stack ps)
{
    
    
	ps._a[0] = 10;
	ps._top++;
}

void test2()
{
    
    
	Stack s={
    
    0,0,0};
	StackInit(&s);
	push(s);
	printf("%d", s._a[0]);
}

The StackInit function here is just a simple initialization to open up space for the stack, and the final running result is 10. The reason is that in the push function, although the value is passed, the member _a in the ps structure still has the ability to change the heap memory , which can be represented by the following figure

insert image description here
So now switch to C++, after introducing the concept of class, the whole becomes a bit more complicated than C language, the reasons are as follows:

First, define a stack class and complete a series of stack operations

typedef int STDataType;
class Stack
{
    
    
public:
	Stack()
	{
    
    
		capacity = 4;
		a = (STDataType*)malloc(sizeof(STDataType) * capacity);
		if (a == nullptr)
		{
    
    
			perror("malloc fail");
			exit(-1);
		}
		top = 0;
	}
	~Stack()
	{
    
    
		top = capacity = 0;
		free(a);
		a = nullptr;
	}
	void Push(STDataType x)
	{
    
    
		if (capacity == top)
		{
    
    
			capacity *= 2;
			STDataType* tmp = nullptr;
			tmp = (STDataType*)realloc(a,sizeof(STDataType) * capacity);
			if (tmp == nullptr)
			{
    
    
				perror("realloc fail");
				exit(-1);
			}
			a = tmp;
		}
		a[top] = x;
		top++;
	}
	void Pop()
	{
    
    
		top--;
	}
	STDataType Top()
	{
    
    
		return a[top];
	}
private:
	STDataType* a;
	int top;
	int capacity;
};

It is the same as the implementation of C language. If we directly copy by value, the specific method is as follows:

void func1(Stack s)
{
    
    
   s.Push(1);
}

int main()
{
    
    
   Stack s1;
   func1(s1);
   return 0;
}

Then draw a picture similar to the above

insert image description here
It seems to be basically the same as the C language, but the actual difference is very big. C++ will execute the constructor and destructor. Then after entering the stack frame of func1, the destructor will be executed when the stack frame is destroyed. The space pointed to by _a is destroyed, then after returning to the stack frame of the main function, the destructor is still required to end the program. At this time, _a has been destroyed once, and the program will crash and cannot run normally.

This actually means that it is not easy to directly copy objects in C++. If two objects point to the same space, problems will inevitably arise. C++ grammar defines a copy function to solve this problem.

Traits of the copy constructor

The copy constructor is also a special member function, as shown in:

  1. The copy function is an overload of the constructor
  2. The copy function has only one parameter and must be a reference to a class type object. The compiler will report an error when using the pass-by-value method because infinite recursive calls are involved.
  3. If not explicitly defined, the compiler will generate a default copy constructor. The default copy constructor will complete the copy according to the storage byte order of the object in memory, also known as shallow copy or value copy
  4. Deep copy involves the problem of space on the heap above the stack
  5. Typical application scenarios of copy constructor

Create a new object using an existing object.
The function parameter type is a class type object.
The function return value type is a class type object.

The following is an analysis based on the characteristics of the copy constructor

1. The copy constructor is an overload of the constructor

This is easy to explain. The function name of the copy constructor is the same as that of the constructor, but the function parameters are different.

2. The copy function has only one parameter and must be a reference to a class type object. The compiler will report an error when using the pass-by-value method because it involves infinite recursive calls

Suppose we implement the copy constructor like this here:

//函数定义
Date(const Date d1)
{
    
    
	_day = d1._day;
	_month = d1._month;
	_year = d1._year;
}
//函数调用
Date d1(d2);

insert image description here
So how is the standard writing method written?

//函数定义
Date(const Date& d1)
{
    
    
	_day = d1._day;
	_month = d1._month;
	_year = d1._year;
}
//函数调用
void func2(Date d2)
{
    
    
	d2.Print();
}

int main()
{
    
    
	Date d1(2002, 10, 12);
	d1.Print();
	func2(d1);
	return 0;
}

3. If not explicitly defined, the compiler will generate a default copy constructor. The default copy constructor will complete the copy according to the storage byte order of the object in memory, also called shallow copy or value copy. 4. Deep copy
involves The problem of space on the heap above the stack

The things to note here are:

When the copy constructor is not written, the default copy constructor generated by compilation is different from the previous constructor features

  1. built-in types are value copies
  2. A custom type is called its copy

To put it simply, types like Date do not require us to perform copy construction, but types of Stack require deep copying

The following is the copy constructor of deep copy

Stack(const Stack& s1)
{
    
    
	top = s1.top;
	capacity = s1.capacity;
	STDataType* tmp = (STDataType*)malloc(sizeof(STDataType) * capacity);
	if (tmp == nullptr)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	a = tmp;
}

The so-called deep copy is to re-create a space on the heap for the constructed stack, so as to avoid the situation where the stack in the function stack frame is freed at the end and the space on the heap is dropped, causing the main function to crash

Note: If there is no resource application involved in the class, the copy constructor can be written or not; once the resource application is involved, the copy constructor must be written, otherwise it is a shallow copy.

operator overloading

C++'s improvement on the basis of C can also be reflected in operator overloading

C++ introduces operator overloading to enhance the readability of the code. Operator overloading is a function with a special function name, and also has its return value type, function name, and parameter list. The return value type and parameter list are similar to ordinary functions.
The function name is: the keyword operator followed by the operator symbol that needs to be overloaded.
Function prototype: return value type operator operator (parameter list)

Suppose we now want to compare the dates who is bigger, then this scenario can apply operator overloading

// 类体内定义运算符重载
bool operator <(const Date& d1)
{
    
    
	if (_year > d1._year)
	{
    
    
		return false;
	}
	else if (_year == d1._year && _month > d1._month)
	{
    
    
		return false;
	}
	else if (_year == d1._year && _month == d1._month && _day > d1._day)
	{
    
    
		return false;
	}
	else
	{
    
    
		return true;
	}
}

// 调用main函数
int main()
{
    
    
	int i = 10;
	int j = 20;
	int tmp1 = i < j;
	Date d1(2000, 10, 20);
	Date d2(2001, 8, 10);
	int tmp2 = d1 < d2;
	cout << tmp1 << endl;
	cout << tmp2 << endl;
}

We go to assembly observation

insert image description here
It is not difficult to find that calling less than after operator overloading actually calls the operator overloading function

This greatly improves the readability of the code

assignment operator overloading

What you need to know about copy construction and assignment operator overloading...

1. As far as operator overloading itself is concerned, the function itself is a member function

As far as the above code is concerned, suppose the following command is executed here

d1<d2

In fact, when implemented, it will be converted into this:

d1.operator<(d2)

Then return the corresponding value or other forms

2. The difference between assignment operator overloading and copy construction

Earlier we said that the copy construction needs to be referenced, and it will be recursive without references

Date d1(const Date d2);            //错误写法
Date d1(const Date& d2);           //正确写法

So in contrast to operator overloading, it looks like this on the surface:

bool operator <(const Date d1)
bool operator <(const Date& d1)

Is the above writing method OK? In fact, it is possible. Here we need to have a deeper understanding of the infinite loop of copy construction.

For copy construction, it essentially needs to create a new object, that is to say, when performing a copy constructor here, you must first pass parameters and then execute the function to create a new object, and in the process of passing parameters, you will fall into whether this is also a copy In the cycle of construction, it will eventually fall into an infinite loop
and cause the construction to fail. For operator overloading, this is just to execute a member function in a class, and both objects have been created, and the Date passed here is just to pass the parameter It’s just over, if you don’t use a reference, you will create a formal parameter, and there will be more consumption in memory. Memory consumption is reduced, so it is not necessary to pass by reference here, and pass by reference is just a way to improve efficiency

The meaning of const

The const-modified "member function" is called a const member function. A const-modified class member function actually modifies the implicit this pointer of the member function, indicating that any member of the class cannot be modified in the member function. Analyze the actual situation

Address and const address operator overloading

This is the last default member function, but there are very few usage scenarios.
Generally, these two operators do not need to be overloaded. You can use the default address-taking overload generated by the compiler. Only in special cases, you need to overload

Guess you like

Origin blog.csdn.net/qq_73899585/article/details/131865720