[C ++ class and object one]-constructor and destructor

When it comes to constructors and destructors, do you know what they are and what they do? First of all, we use the oop idea to simulate and implement a sequential stack. The code is implemented as follows:

class SeqStack
{
public:
	//对象成员变量的初始化操作
	void init(int size = 10)
	{
		_pstack = new int[size];
		_top = -1;
		_size = size;
	} 

	//释放对象成员变量占用的外部堆内存(外部资源)
	void release()
	{
		delete[] _pstack;
		_pstack = nullptr;
	}
	void push(int val)
	{
		if (full())
			resize();
		_pstack[++_top] = val;
	}
	void pop()
	{
		if (empty())
			return;
		--_top;
	}
	int top()
	{
		return _pstack[_top];
	}
	bool empty() { return _top == -1; }
	bool full() { return _top == _size - 1; }
private:
	int* _pstack;//动态开辟数组,存储顺序栈的元素
	int _top;//指向栈顶元素的位置
	int _size;//数组扩容的总大小
	void resize()
	{
		int* ptmp = new int[_size * 2];
		for (int i = 0; i < _size; ++i)
		{
			ptmp[i] = _pstack[i];
		}
		delete[]_pstack;
		_pstack = ptmp;
		_size *= 2;
	}
};
int main()
{
	SeqStack s;
	s.init(5);
	for (int i = 0; i < 15; ++i)
	{
		s.push(rand() % 100);
	}
	while (!s.empty())
	{
		cout << s.top() << " ";
		s.pop();
	}
	return 0;
}

The results are as follows:
Insert picture description here
But after the seemingly perfect code, there are many problems. In the face of many member methods, you may even forget what you wrote. For example, the release method was completely forgotten by us. If you think about it carefully, the memory opened on our heap has not been freed up, which can easily cause the program to crash. So every time you need to manually initialize the object and release the resource of the object. Is there any good way to solve this problem? From this, we introduce the concept of constructor and destructor.

1. What is a constructor and destructor

The name of the constructor and destructor is different from the class name, and there is no return value.

1. Definition

Constructor : mainly used to initialize the object when it is created, that is, to assign initial values ​​to object member variables, and it is always used in the statement that creates the object together with the new operator.
It can take parameters , so it can provide multiple constructors, which can be overloaded and have the same function as init . The constructor generates the object as soon as it is called

SeqStack(int size = 10)
	{
		cout << this << "SeqStack()" << endl;
		_pstack = new int[size];
		_top = -1;
		_size = size;
	}

Destructor : Contrary to the constructor, when the object ends its life cycle, if the function where the object is located has been called, the system automatically performs the destructor. ** The destructor cannot be overloaded. No parameters, so the destructor can only be called automatically when one out of scope, the object no longer exists

~SeqStack()
	{
		cout << this << "~SeqStack()" << endl;
		delete[] _pstack;
		_pstack = nullptr;
	}

2. Points for attention

1. The call of the constructor can use no object, and match according to the incoming parameters

2. Construct first, then destruct, and then construct first.
For example: the above code, we add another object in the main function, the code implementation is as follows:

int main()
{
	SeqStack s;
	for (int i = 0; i < 15; ++i)
	{
		s.push(rand() % 100);
	}
	while (!s.empty())
	{
		cout << s.top() << " ";
		s.pop();
	}
	SeqStack s1(50);
	return 0;
}

The results are as follows:
Insert picture description here
We can find that the s object constructed first is destructed last.

3. The system will generate constructor and destructor without parameters by default. When the class used does not occupy external space, you don't need to define the destructor yourself.

4. The call to the destructor can have an object , because the object has not disappeared before the destructor is called.

5. However, if the destructor is called according to the object, there will be illegal access to the heap memory at this time, so it is not recommended to call the destructor yourself.
Such as executing these two lines of code s1. ~ SeqStack (); s1.push (30); The
analysis is as follows:
Insert picture description here

Second, the initialization list of the constructor

1. Implement a commodity class

When we want to define a commodity class, the code is as follows:

class CGoods
{
public:
	CGoods(const char* n, int a, double p)
	{
		strcpy(_name, n);
		_amount = a;
		_price = p;
	}
	void show()
	{
		cout << "name: " << _name << endl;
		cout << "amount:" << _amount << endl;
		cout << "price:" << _price << endl;
		_data.show();
	}
private:
	char _name[20];
	int _amount;
	double _price;
};

But on this basis, I want to add a commodity date information as follows:

class CDate
{
public:
	CDate(int y, int m, int d)
	{
		_year = y;
		_month = m;
		_day = d;
	}
	void show()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

The date is part of the product information a part of ... how to combine the two parts?

We use the member object as follows:
add CDate _data; member object in the CGoods class private.

But this is not enough. Compile will report an error saying that CDate does not have a suitable default constructor available

Because we all know that the generation of an object is divided into two parts: allocating memory and calling a constructor. When we do not specify a constructor, the compiler calls the default constructor, but because we already have a constructor, it will not be generated. The default constructor. Therefore, we need to improve the above constructor. The improved code is as follows:

CGoods(const char* n, int a, double p,int y,int m,int d)
		:_data(y,m,d)
	{
		//当前类类型构造函数体
		strcpy(_name, n);
		_amount = a;
		_price = p;
	}

Specify the construction method on the data in this way, also called the initialization list of the constructor : you can specify the initialization method of the current object member variables (especially for member objects)

2. The difference between the initialization list of the constructor and the constructor body of the current class type

In general, take the member variable _amout

  • The initialization list of the constructor is equivalent to the initialization of the variable when it is defined, similar to int _amount = a;
  • The class type constructor body is equivalent to int _amount; int _amount = a;

But there is no difference for disassembly if it is a general type, but it must be written to the initialization list for the class type,
so the constructor of the commodity class can also be changed to the following way

CGoods(char* n, int a, double p, int y, int m, int d)
	:_data(y, m, d)//CData _data(y,m,d);
	,_amount(a)
	,_price(p)
{
	strcpy(_name, n);
}

3. Exercises for the initialization list of the constructor

The following code, what is the running result?

class Test
{
public:
	Test(int data = 10):mb(data),ma(mb){}
	void show()
	{
		cout << "ma:" << ma << " mb:" << mb<<endl;
	}
private:
	int ma;
	int mb;
};
int main()
{
	Test t;
	t.show();
	return 0;
}

The results are as follows:
Insert picture description here
Reason: In the VS compiler, the development on the stack is initialized to 0xcccccccc = -858993460. When an object is generated, the initialization method of the member variables of the object is related to the order in which they are defined, and it is in the constructor initialization list The order of appearance is irrelevant

3. Life cycle of different objects

1. Local objects on the stack : call the constructor when it is defined, and call the destructor out of the function scope. For example, the above code is implemented.

2. The global object (.data section) on the stack : call the constructor when it is defined, and only destruct it when the program ends.
For example, the above code prototype, add a global object gs. His construction destructor call is as follows:
Insert picture description here

3. Objects on the heap : when defined: ①. Malloc opens up memory ②. Call construction; when released: ①. Call destructor ②. Then release the memory.
For example, the above code prototype, add an object ps on the heap. His construction destructor call is as follows:

SeqStack* ps = new SeqStack(60);//开辟在(.heap)段里面malloc内存开辟+SeqStack(60)对象构造操作
	ps->push(70);
	ps->push(80);
	ps->pop();
	cout << ps->top() << endl;
	delete ps;

According to the operation results, we can find that he only has construction without destructuring,
because the objects on the heap must be manually released by themselves (first call ps-> ~ SeqStack () and then free (ps))

Published 98 original articles · won praise 9 · views 3673

Guess you like

Origin blog.csdn.net/qq_43412060/article/details/105065123