C++ Classes and Objects-2 (Constructors and Destructors) (Super Detailed)

Constructor and destructor - super detailed explanation

1. Constructor

1.1 Concept

For the following Date class:

class Date
{
    
    
public:
	void Init(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		 cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1;
	d1.Init(2022, 7, 5);
	d1.Print();
	Date d2;
	d2.Init(2022, 7, 6);
	d2.Print();
	return 0;
}

For the Date class, you can set the date for the object through the Init public method. However, it would be a bit troublesome to call this method to set the information every time the object is created. Can the information be set when the object is created?

The constructor is a special member function with the same name as the class. It is automatically called by the compiler when creating a class type object to ensure that each data member has an appropriate initial value and is only called once during the entire life cycle of the object. .

1.2 Features

The constructor is a special member function. It should be noted that although the name of the constructor is construction, the main task of the constructor is not open space to create objects, but to initialize the objects.

Its characteristics are as follows:

1. The function name is the same as the class name.
2. No return value.
3. The compiler automatically calls the corresponding constructor when the object is instantiated.
4. Constructors can be overloaded.
(If you are not sure about overloading, you can click here -> C++ function overloading )

Example:

class Date
{
    
    
public:
    // 1.无参构造函数
    Date()
    {
    
    }

    // 2.带参构造函数
    Date(int year, int month, int day)
    {
    
    
        _year = year;
        _month = month;
        _day = day;
    }
    
    // 3.带缺省值的构造函数
    Date(int year=2023, int month=1, int day=1)
    {
    
    
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};

void TestDate()
{
    
    
    Date d1; // 调用无参构造函数
    Date d2(2015, 1, 1); // 调用带参的构造函数
    // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
    Date d3();
}

Note: If you create an object through a no-argument constructor, there is no need to follow the object with parentheses, otherwise it will become a function declaration.

5. If there is no explicitly defined constructor in the class, the C++ compiler will automatically generate a parameterless default constructor. Once the user explicitly defines it, the compiler will no longer generate it.

Example:

5.1 After shielding the constructor in the Date class, the code can be compiled because the compiler generates a default constructor with no parameters and assigns random values ​​to the object (different compilers may assign values ​​in different ways, and some compilers will initialize is 0)

class Date
{
    
    
public:
    // 如果用户显式定义了构造函数,编译器将不再生成
    /*Date(int year, int month, int day)
    {
    _year = year;
    _month = month;
    _day = day;
    }*/
    
    void Print()
    {
    
    
        cout << _year << "/" << _month << "/" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    
    
    Date d;
    d.Print();
	return 0;
}

Insert image description here


5.2 Release the constructor in the Date class. Since the explicitly defined constructor takes formal parameters, the code compilation fails because once any constructor is explicitly defined, the compiler will no longer generate it. No-argument constructor, error will be reported after releasing: error C2512: "Date": No suitable default constructor is available.

class Date
{
    
    
public:
    void Print()
    {
    
    
        cout << _year << "/" << _month << "/" << _day << endl;
    }
    // 如果用户显式定义了构造函数,编译器将不再生成
    Date(int year, int month, int day)
    {
    
    
    _year = year;
    _month = month;
    _day = day;
    }
    
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    
    
    Date d;//此时这里会报错
    d.Print();
	return 0;
}

Insert image description here


6. Regarding the default member functions generated by the compiler, many veterans will have doubts: if the constructor is not implemented, the compiler will generate a default constructor. But it seems that the default constructor is useless? The d object calls the default constructor generated by the compiler, but the d object _year/_month/_day is still a random value. In other words, the default constructor generated by the compiler is of no use here? ?
Answer: C++ divides types into built-in types (basic types) and custom types. Built-in types are data types provided by the language
, such as: int/char..., custom types are types we define ourselves using class/struct/union, etc. If you look at the
following program, you will find that the compiler generates a default constructor The default member function of the custom type member _t will be called
.

class Time
{
    
    
public:
    Time()
    {
    
    
        cout << "Time()" << endl;
        _hour = 0;
        _minute = 0;
        _second = 0;
    }
private:
    int _hour;
    int _minute;
    int _second;
};
class Date
{
    
    
private:
    // 基本类型(内置类型)
    int _year;
    int _month;
    int _day;
    // 自定义类型
    Time _t;
};
int main()
{
    
    
    Date d;
    return 0;
}

Insert image description here
Note: In C++11, a patch has been made for the defect that built-in type members are not initialized, that is, built-in type member variables can be given default values ​​when declared in a class.

class Time
{
    
    
public:
	Time()
	{
    
    
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
    
    
private:
	// 基本类型(内置类型)【内置类型成员变量在类中声明时可以给默认值】
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
    
    
	Date d;
	return 0;
}

7. The parameterless constructor and the fully default constructor are both called default constructors, and there can only be one default constructor.

class Date
{
    
    
public:
	Date()
	{
    
    
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
// 以下测试函数能通过编译吗?
void Test()
{
    
    
	Date d1;
}

Insert image description here

Note: No-argument constructors, all-default constructors, and constructors generated by default by the compiler that we have not written can all be considered
default constructors.

2. Destructor

2.1 Concept

Through the previous study of constructors, we know how an object comes from, and how does that object disappear?

Destructor: Contrary to the function of the constructor, the destructor does not complete the destruction of the object itself. The local object destruction is completed by the compiler. When the object is destroyed, the destructor will be automatically called to complete the cleanup of resources in the object.

2.2 Features

The destructor is a special member function with the following characteristics:

1. The destructor name is preceded by the character ~ before the class name.
2. No parameters and no return value type.
3. A class can only have one destructor. If not explicitly defined, the system will automatically generate a default destructor. Note: Destructors cannot be overloaded.
4. When the object life cycle ends, the C++ compilation system automatically calls the destructor.


typedef int DataType;
class Stack
{
    
    
public:
	Stack(size_t capacity = 3)
	{
    
    
		cout << "Stack()" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
    
    
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
    
    
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	// 其他方法...
	~Stack()
	{
    
    
		cout << "~Stack()" << endl;
		if (_array)
		{
    
    
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
void TestStack()
{
    
    
	Stack s;
	s.Push(1);
	s.Push(2);
}
int main()
{
    
    
	TestStack();
}

Insert image description here


5. Regarding the destructor automatically generated by the compiler, will it accomplish something? In the following program, we will see that the default destructor generated by the compiler calls its destructor for a custom type member.

class Time
{
    
    
public:
	~Time()
	{
    
    
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
    
    
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
    
    
	Date d;
	return 0;

Output after the program finishes running: ~Time()

There is no object of Time class directly created in the main method. Why is the destructor of Time class called in the end?

Because: the Date object d is created in the main method, and d contains 4 member variables, of which _year, _month, and _day are built-in type members. There is no need to clean up resources when they are destroyed. Finally, the system can directly recycle its memory. ;
And _t is a Time class object, so when d is destroyed, the _t object of the Time class contained inside it must be destroyed, so the destructor of the Time class must be called.
However: the destructor of the Time class cannot be directly called in the main function. What is actually to be released is the Date class object, so the compiler will call the destructor of the Date class. If Date is not provided explicitly, the compiler will give the Date class Generate a default destructor with the purpose of calling the destructor of the Time class internally, that is, when the Date object is destroyed, it is necessary to ensure that each custom object inside it can be destroyed correctly.
The main function does not directly call the Time class destructor, but explicitly calls the default destructor generated by the compiler for the Date class.

Note: The destructor of which class is called when an object of that class is created, and the destructor of that class is called when an object of that class is destroyed.

6. If there is no resource application in the class, the destructor does not need to be written, and the default destructor generated by the compiler can be directly used, such as the Date class; when there is a resource application, it must be written, otherwise it will cause resource leakage, such as the Stack class. .

(End of chapter)

Guess you like

Origin blog.csdn.net/originalHSL/article/details/131878496