[OOP] Hàm tạo (constructor), hàm hủy (destructor) trong C++

Hàm tạo – Constructor

Constructor là 1 hàm đặc biệt của class, nó được tự động gọi khi 1 đối tượng của class được khởi tạo. Hàm Constructor thường để khởi tạo giá trị ban đầu cho các biến thành viên hoặc setup những vấn đề cần thiết cho class.

Hàm Constructor không giống các hàm thành viên khác, phải theo quy tắc.

  • Tên của constructor phải giống tên của class.
  • Constructor không có kiểu giả trị trả về (kể cả void)
  • Một class có thể có nhiều constructor.

Ví dụ : 

class Date

{

private:

   int day;

   int month;

   int year;

public :

   //Hàm khởi tạo không có tham số
   Date()

   {

      day = 1;

      month = 1;

      year = 1900;

   }

};

Hàm khởi tạo mặc định (Default constructor)

Khi định nghĩa class nếu ta không định nghĩa bất cứ hàm khởi tạo nào cho class thì trong C++ sẽ cung cấp cho ta 1 hàm khởi tạo gọi là hàm khởi tạo mặc định (Default Constructor). Hàm khởi tạo mặc định sẽ không có tham số truyền vào, và không làm gì cả (không gán các giá trị của biến thành viên về mặc định).

Ví dụ


#include <iostream>

using namespace std;

class Date

{

private:

   int m_day;

   int m_month;

   int m_year;

public:

   void Display()

   {

      cout << m_day << "/" << m_month << "/" << m_year;

   }

};

int main()

{

   //Ham khoi tao mac dinh duoc goi

   Date dt;

   dt.Display();

   return 0;

}

Kết quả:

-858993460/-858993460/-858993460

Trong ví dụ trên ta không định nghĩa bất cứ hàm khởi tạo nào trong class Date, C++ sẽ cung cấp cho ta 1 hàm khởi tạo mặc định (không có parameter) được gọi khi ta khởi tạo đối tượng. Các biến thành viên sẽ có giá trị ngẫu nhiên.

Ta xét ví dụ tiếp theo.


#include <iostream>

using namespace std;

class Date

{

public:

   int m_day;

   int m_month;

   int m_year;

public:

   Date(int day, int month, int year)

   {

      m_day = day;

      m_month = month;

      m_year = year;
 
   }

   void Display()

   {

      cout << m_day << "/" << m_month << "/" << m_year;

   }

};

int main()

{

   //Error vì C++ sẽ không cung cấp hàm khởi tạo mặc định
   Date dt;

   //Gọi hàm khởi tạo có 3 tham số
   Date dt1(6, 1, 2016);

   dt.Display();

   return 0;

}

Trong ví dụ trên trong class Date ta định nghĩa 1 hàm khởi tạo có 3 tham số, khi đó C++ không cung cấp 1 hàm khởi tạo mặc định. Vì vậy khi tạo đối tượng ta phải truyền đủ đối số để gọi đúng hàm khởi tạo đã được định nghĩa.

Danh sách khởi tạo thành viên (member initializer lists)

Trong C++ ta có thể khởi tạo giá trị ban đầu của các biến thành viên bằng cách gán giá trị cho chúng trong hàm khởi tạo.

Ví dụ :


#include <iostream>

using namespace std;

class Date

{

public:

   int m_day;

   int m_month;

   int m_year;

public:

   Date()

   {

      m_day = 1;

      m_month = 1;

      m_year = 1990;

   }

   void Display()

   {

      cout << m_day << "/" << m_month << "/" << m_year;

   }

};

Một cách khác trong C++ cung cấp cho ta 1 cách khởi tạo trực tiếp các giá trị của biến thành viên gọi là Initializer lists.

Ví dụ:


#include <iostream>

using namespace std;

class Date

{

private:

   int m_day;

   int m_month;

   int m_year;

public:

   Date() : m_day(1), m_month(1), m_year(1900)

   {

      //Không cần gán các giá trị của biến thành viên trong constructor

   }

   void Display()

   {

      cout << m_day << "/" << m_month << "/" << m_year;

   }

};

Vậy 2 cách trên có gì khác nhau?

Cách 1 : Gán giá trị cho các biến thành viên.

Cách 2 : Khởi tạo trực tiếp giá trị cho các biến thành viên.

Xét ví dụ:


//Correct
const float PI = 3.14

//Error
const float PI;

PI = 3.14;

Theo ví dụ trên ta chỉ có thể khởi tạo giá trị của hằng số khi khai báo, không thể gán giá trị cho hằng số. Với Initializer list là ta khởi tạo giá trị cho các biến thành viên không phải gán giá trị ban đầu cho các viên thành viên.

Ta có thể khởi tạo giá trị cho hằng số bằng Initializer list:

class Math

{

private :

   const float PI;

public :

   //Correct
   Math() : PI(3.14)

   {

      //TODO

   }

};

Nhưng không thể gán giá trị cho hằng số trong constructor:

class Math

{

private :

   const float PI;

public :

   Math()

   {

      //Error
      PI = 3.14

   }

};

Hàm hủy – Destructor

Hàm hủy là hàm đặc biêt của class được tự động gọi khi ta hủy đổi tượng. Nếu như hàm tạo khởi tạo các giá trị cho class thì hàm hủy xóa các dữ liệu của class.

Hàm hủy được xây dựng tuân theo quy tắc:

  • Tên hàm trùng với tên class, trước tên hàm có dấu ‘~’.
  • Hàm không có dữ liệu trả về.
  • Hàm hủy không có tham số.

Ví dụ :


#include <iostream>

using namespace std;

class Test

{

private:

   int* arr;

   int nId;

public:

   Test(int id)

   {

      //Cap phat bo nho cho mang trong ham tao

      arr = new int[10];

      nId = id;

      cout << "Day la ham tao" << nId << endl;

   }

   ~Test()

   {

      cout << "Day la ham huy" << nId << endl;

      //Giai phong bo nho trong ham huy

      delete[] arr;

   }

};

int main()

{

   //Ham tao duoc goi
   Test tes1(1);

   //Toan tu new goi ham tao cho ptrTest
   Test* ptrTest = new Test(2);

   //Toan tu delete goi ham huy cho ptrTest
   delete ptrTest;

   return 0;

}

Kết quả:

Day la ham tao1

Day la ham tao2

Day la ham huy2

Day la ham huy1

Hàm hủy của test1 gọi sau hàm hủy của ptrTest ví hàm hủy của ptrTest được gọi khi ta gọi toán tử delete còn hàm hủy của tes1 được gọi khi ta kết thúc hàm main, vì khi đó tes1 mới bị hủy.

Was this article helpful?

Leave A Comment?