Classes and objects [2] default member functions

introduction

In the previous article, I briefly introduced classes and objects: poking me to see classes and objects
for the first time It is not difficult to find that class types greatly facilitate the user's use and interaction with objects. For example, the linked list, stack and queue we implemented before.

For built-in types, there are operators for initialization, assignment, and more. But for custom types, we need to use functions to implement operations such as initialization, assignment, and destruction. But it is cumbersome to implement and call these basic universal functions explicitly when each class is used, so when a class type is created, 6 default member functions are automatically generated: constructor, destructor, copy construction , Assignment overloading, address taking and const address taking , and they are automatically called where these functions are needed.
insert image description here

This article focuses on the first four default member functions:

Constructor

The function name of the constructor is the same as the class name, and its function is to initialize the class object.
The constructor will be called automatically by the compiler when the class object is created to ensure that each class object has a suitable initial value. The constructor is called only once per object's lifetime.

definition

The function name of the constructor is the same as the class name, has no return value, supports function overloading and default parameters .
For example, the constructor of the date class Date simply written in the previous article:

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

int main()
{
    
    
	Date d1(2023, 2, 10);
	return 0;
}

insert image description here

Default constructors and related issues

Although the constructor is called automatically, we need to implement it. If it is not implemented, the compiler will generate a default constructor with no parameters.
The default constructor is a no-argument or all-default constructor . For a class type with a default constructor, there is no need to pass parameters when creating a class object (as in the above example).

It should be noted that when a constructor is defined, the compiler will not generate a default constructor without parameters. So if you do not create a class object with an initial value in the above example, an error will be reported:

int main()
{
    
    
	Date d1(2023, 2, 10);//正确代码:给初始值会调用有参的构造函数
	//Date d2; 错误代码:无合适的默认构造函数可用
	return 0;
}

insert image description here
In this case, we can define this parameterized constructor as a fully default default constructor, so that we can use the default parameter value of the default construction without giving an initial value when calling:

class Date
{
    
    
public:
	Date(int year = 2023, int month = 5, int day = 21)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}

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

int main()
{
    
    
	Date d1(2023, 2, 10);
	Date d2;
	return 0;
}

insert image description here
So, what effect will the default constructor with no parameters generated by the compiler achieve?
For built-in types, this no-argument default constructor cannot be implemented for the initial value, and for custom types, its own default constructor will be called.
This makes the automatically generated default construction very tasteless and cannot achieve our purpose of initializing class objects. So in the C11 standard, we can give a default value when declaring a variable in a class object, and this default value will be used to initialize the class object in the default construction generated by the compiler (this default value is actually initialized in the constructor The ones used in the list will be mentioned later):

class Date
{
    
    
public:
private:
	int _year = 2023;
	int _month = 2;
	int _day = 10;
};

int main()
{
    
    
	Date d2;
	return 0;
}

insert image description here

destructor

The destructor is automatically called at the end of the object's life cycle to destroy the resources in the class object. (Note that this is to destroy the resources in the object, not the object itself)
For the resources in the class type are all built-in types, and there is no application space, the compiler will automatically release this space after the life cycle of the class object ends; while for Classes with application space must implement destructors to release resource space:

The function name of the destructor ~类名has no return value and cannot be overloaded.
Such as the destruction of this stack object:

class Stack
{
    
    
public:
	Stack()
	{
    
    
		_top = 0;
		_capacity = 10;
		_data = new int[_capacity] {
    
    0}; //new动态申请空间
	}
	~Stack()
	{
    
    
		_top = 0;
		_capacity = 0;
		delete[] _data; //delete释放new申请的空间
	}
private:
	int* _data;
	int _top;
	int _capacity;
};
int main()
{
    
    
	Stack s1;
	return 0;
}

insert image description here
Of course, when a destructor is not implemented, the compiler will automatically generate one. Of course, the default generated destructor does not deal with built-in types, and it will call its own destructor for custom types.

copy construction

definition

Copy construction is an overloaded form of the constructor, with only a single parameter , that is, a reference to the type of this class (generally modified with const so that constants can be passed).
Called automatically by the compiler when initializing a class object to create a new class object of the same type (that is, whenever a class object needs to be copied):

class Date
{
    
    
public:
	Date(int year = 2023, int month = 2, int day = 10)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
    
    
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year = 2023;
	int _month = 2;
	int _day = 10;
};
int main()
{
    
    
	Date d1;
	Date d2(d1);
	return 0;
}

insert image description here

Problems that may arise when using

  1. The parameters of the copy constructor must be passed by reference, not by value.
    The copy constructor will be called automatically when a class object copy operation occurs . , copy when returning a value, copy when casting a cast , etc.
    If you use value passing when implementing the copy constructor, you will need to copy and call the copy construction, and the copy behavior will occur during the copy construction, and then the copy behavior will occur again... It will be called infinitely, so the copy constructor must pass quote.

  2. Problems with Compiler-Generated Copy Construction
    Of course, if you don't implement a copy constructor, the compiler will generate one. This automatically generated copy construction is implemented in a byte-by-byte copy manner , that is to say, for variables without application space, copying can be achieved, such as this date class:

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

int main()
{
    
    
	Date d1;
	Date d2(d1);
	return 0;
}

insert image description here
However, when there is a resource application in the class object, the default copy structure can only copy the pointer variable pointing to the space byte by byte, instead of opening another space to copy the requested space. content.
Not only that, because there are two member variables of class objects pointing to the same space, the destructor will be called twice after the compilation is completed, and this space will be released twice, which will obviously cause problems. For example this stack class:

class Stack
{
    
    
public:
	Stack()
	{
    
    
		_top = 0;
		_capacity = 10;
		_data = new int[_capacity] {
    
    0};
	}
	~Stack()
	{
    
    
		_top = 0;
		_capacity = 0;
		delete[] _data;
	}
private:
	int* _data;
	int _top;
	int _capacity;
};
int main()
{
    
    
	Stack s1;
	Stack s2(s1);
	return 0;
}

insert image description here
insert image description here
In this case, you must implement the copy constructor yourself, which will be mentioned later.

assignment operator overloading

Before introducing operator overloading, we need to understand the knowledge of operator overloading:

operator overloading

For built-in types, there are a variety of operators to help us achieve operations on built-in types. For example, the addition of two ints a + bcan be realized by using it, which is not only very concise, but also highly readable;
but for custom types, we can only write some functions to achieve it. For example Add(Date& d, int day), the only way to call Add(d, 100);it is obviously not very readable, and it is also very troublesome to use. So C++ introduced operator overloading:

Operator overloading is a function with a special function name, whose parameter list and return value are consistent with ordinary functions:返回值 operator操作符(参数列表); (operator is a keyword, due to operator overloading)

have to be aware of is:

  1. New operators cannot be created by concatenating other symbols: such as operator@;
  2. An overloaded operator must have a class type parameter;
  3. operators for built-in types whose meaning cannot be changed (also for readability);
  4. When overloaded as a class member function, its formal parameters seem to be 1 less than the number of operands, because the first parameter of the member function is the hidden this;
  5. .* :: sizeof ?: .The above 5 operators cannot be overloaded.
class Date
{
    
    
public:
	Date(int year = 2023, int month = 2, int day = 10)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator==(const Date& d)
	{
    
    
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
private:
	int _year = 2023;
	int _month = 2;
	int _day = 10;
};

int main()
{
    
    
	Date d1;
	Date d2(d1);
	cout << (d1 == d2) << endl; //相当于d1.operator==(d2)
	return 0;
}

insert image description here
In the above code, == is overloaded to judge whether two date classes are equal.
Because private member variables cannot be accessed outside the class, this overload is defined in the class. d1 == d2In essence, it is to call the member function of object d1: d1.operator==(d2).

assignment operator overloading

Assignment operator overloading means overloading = :

  1. only one parameter const T&;
  2. The return value T&is convenient for continuous assignment, making it more like =;
  3. Assignment operator overloading must be a member function, not a global function, because it is a default member function, if it is global, it will conflict with the automatically generated class (other default member functions are the same);
  4. The assignment operator overloads automatically generated by the compiler can only implement byte-by-byte assignments, that is, shallow copies (similar to the copy construction generated by default):
class Date
{
    
    
public:
	Date(int year = 2023, int month = 2, int day = 10)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator==(const Date& d)
	{
    
    
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
	Date& operator=(const Date& d)
	{
    
    
		_year = d._year;
		_month = d._month;
		_day = d._day;
	    return *this;
	}
private:
	int _year = 2023;
	int _month = 2;
	int _day = 10;
};

int main()
{
    
    
	Date d1;
	Date d2(2023, 5, 21);
	d2 = d1;
	return 0;
}

insert image description here

Summarize

At this point, the introduction of default member functions is over
. I believe you can realize that default member functions can indeed make it more convenient for us to use class objects.

If you think that I did not introduce a certain part clearly or that there is a problem with a certain part, you are welcome to raise it in the comment area

If this article is helpful to you, I hope it will be connected with one click

Hope to make progress together with you

Guess you like

Origin blog.csdn.net/weixin_73450183/article/details/130692940