C++ Programming – Destructors


Những bài trước chúng ta đã có chút kiến thức nhất định về constructor và bây giờ chúng ta sẽ học về destructor.

Destructor cũng là một hàm đặc biệt trong class giống như constructor, và nó sẽ thực thi khi object bị phá hủy (destroy). Constructor được thiết kế để khởi tạo một class, còn destructor được thiết kế để “dọn dẹpclass (giải phóng bộ nhớ).

Khi một object đi ra khỏi phạm vi (out of scope), hay một object được cấp phát động bị xóa bởi từ khóa delete, thì destructor của class được gọi (nếu tồn tại) để dọn dẹp bất cứ thứ gì cần thiết trước khi object bị remove khỏi bộ nhớ. Đối với các class đơn giản, nơi mà chỉ chứa các biến member bình thường, thì không cần destructor vì C++ sẽ tự động dọn dẹp bộ nhớ cho bạn.

Tuy nhiên, nếu trong class của bạn có chứa những thành phần sau: bộ nhớ động (thường chúng ta hay cấp phát bằng từ khóa new), một đối tượng FILE hay một database handle, thì chúng ta cần có một destructor, và destructor là hàm cuối cùng thực thi trước khi object bị phá hủy.

Destructor naming

Giống như constructor, tên của destructor có một số luật như sau:

  • Destructor phải có tên giống class, và phải có dấu”~” đứng trước.
  • Destructor không được phép có tham số truyền vào (constructor thì vô tư).
  • Destructor không có kiểu trả về.

Luật thứ 2 ngụ ý rằng, trong một class chỉ có duy nhất một destructor, và không có chuyện overload ở đây.

Giống như constructor, destructor không nên được gọi một cách rõ ràng tường mình. Tuy nhiên, các destructor có thể gọi các function khác một cách an toàn vì object không bị phá hủy cho đến khi destructor thực thi.

A destructor example

Chúng ta cùng xem một ví dụ

#include <iostream>
#include <cassert>
 
class IntArray
{
private:
	int *m_array;
	int m_length;
 
public:
	IntArray(int length) // constructor
	{
		assert(length > 0);
 
		m_array = new int[length];
		m_length = length;
	}
 
	~IntArray() // destructor
	{
		// Dynamically delete the array we allocated earlier
		delete[] m_array ;
	}
 
	void setValue(int index, int value) { m_array[index] = value; }
	int& getValue(int index) { return m_array[index]; }
 
	int getLength() { return m_length; }
};
 
int main()
{
	IntArray ar(10); // allocate 10 integers
	for (int count=0; count < 10; ++count)
		ar.setValue(count, count+1);
 
	std::cout << "The value of element 5 is: " << ar.getValue(5);
 
	return 0;
} // ar is destroyed here, 
  // so the ~IntArray() destructor function is called here

Kết quả thực hiện chương trình:
The value of element 5 is: 6

Trong dòng đâu tiên của hàm main, chúng ta thiết lập một mảng tên ar với 10 phần tử kiểu int. Đây là cách thiết lập mảng bằng construtor, nhìn vào phần thân của constructor, nơi thực hiện cấp phát động cho các phần tử mảng. Chúng ta phải thực hiện cấp phát động ở đây, vì khi chương trình biên dịch tới đoạn constructor, chúng ta không biết mảng cần cấp phát bao nhiêu phần tử cả.

Tại phần cuối của hàm main, ar đi ra khỏi phạm vi của nó (out of scope). Và khi kết thúc hàm main, destructor được gọi, và đương nhiên, bộ nhớ đã được cấp phát ban đầu cho mảng được giải phóng. Trong ngôn ngữ C (hay C++ hướng cấu trúc) không có chuyện này, cho nên chúng ta phải giải phóng bộ nhớ bằng lệnh delete (trong C++) hoặc free (trong C).

Constructor and destructor timing

Như đã đề cập, constructor được gọi khi một object được tạo, và destructor được gọi khi một object bị phá hủy. Ví dụ bên dưới, chúng ta sử dụng lệnh cout bên trong constructordestructor để biết khi nào chúng được gọi (đây là một phương pháp debug).

class Simple
{
private:
    int m_nID;
 
public:
    Simple(int nID)
    {
        std::cout << "Constructing Simple " << nID << '\n';
        m_nID = nID;
    }
 
    ~Simple()
    {
        std::cout << "Destructing Simple" << m_nID << '\n';
    }
 
    int getID() { return m_nID; }
};
 
int main()
{
    // Allocate a Simple on the stack
    Simple simple(1);
    std::cout << simple.getID() << '\n';
 
    // Allocate a Simple dynamically
    Simple *pSimple = new Simple(2);
    std::cout << pSimple->getID() << '\n';
    delete pSimple;
 
    return 0;
} // simple goes out of scope here

Kết quả thực hiện chương trình trên:
Constructing Simple 1
1
Constructing Simple 2
2
Destructing Simple 2
Destructing Simple 1

Chú ý rằng “Simple 1” bị phá hủy (giải phóng) sau “Simple 2” bởi vì chúng ta delete pSimple trước khi hàm main kết thúc, và khi hàm main kết thúc, simpe bị phá hủy.

Lưu ý: Những biến toàn cục được khởi tạo trước hàm main và phá hủy sau hàm main.

A warning about the exit() function

Chú ý rằng nến bạn sử dụng hàm exit(), chương trình của bạn sẽ kết thúc ngay lập tứcdestructor sẽ không bao giờ được gọi. Cần thận với hàm này vì nhiều lúc, bạn cần làm một việc gì đó từ destructor.

Bài tiếp theo: Con trỏ this.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s