Constructor và Destructor là gì?
Phương thức khởi tạo (Constructor) hay phương thức hủy (Destructor) là 2 trong số các phương thức mang lại sự tiện lợi khi lập trình với class
.
- Constructor: phương thức tự động được gọi sau khi đối tượng được tạo xong.
- Destructor: phương thức tự động được gọi trước khi đối tượng tiến hành hủy.
Cách sử dụng Constructor và Destructor trong C++
- Constructor: hàm được định nghĩa bằng cách đặt tên trùng với tên
class
và không có kiểu trả về. - Destructor: hàm được định nghĩa bằng cách đặt trùng với tên
class
và thêm ký tự~
vào phía trước.
#include <iostream> class Node { public: // Constructor Node() { std::cout << "Constructor Called"; } // Destructor ~Node() { std::cout << "Destructor Called"; } } int main() { Node node; return 0; }
Sau dòng Node node;
- Đối tượng
node
được tạo ra. - Sau đó
Node()
tự động được gọi. - Đến giai đoạn kết thúc hàm
main()
, đối tượngnode
sẽ được thu hồi nhưng trước đó sẽ tự động gọi~Node()
.
Các trường hợp sử dụng Constructor tăng tính hiệu quả
Constructor và Destructor không bắt buộc phải định nghĩa. Trong những ngữ cảnh không cần thiết thì không cần định nghĩa 2 phương thức này.
Câu chuyện của Constructor
Giả sử Node
là 1 phần tử trong danh sách liên kết, nghĩa là next
sẽ lưu giữ vị trí của node
kế tiếp hoặc đánh dấu là node
cuối cùng (nullptr
).
#include <iostream> class Node { private: Node *next; public: void init() { this->next = nullptr; } } int main() { Node node; node.init(); return 0; }
Dòng node.init()
được gọi sau khi node
được tạo ra, để đảm bảo next
luôn là node
cuối cùng (khi chưa thêm vào danh sách).
Điều này bất tiện vì node.init()
không đáng tin cậy vì có lúc quên gọi, mặc dù điều này là 1 mong muốn mặc định khi 1 node
ra đời thì next
phải là nulltpr
. Trong trường hợp này, constructor sẽ thể hiện được sức mạnh của nó.
#include <iostream> class Node { private: Node *next; public: Node() { this->next = nullptr; } } int main() { Node node; return 0; }
Như vậy không cần phải luôn tự nhắc bản thân gán nullptr
sau khi node
được tạo ra.
Câu chuyện của Destructor
Với ví dụ về Node trong danh sách liên kết, thấy được tính hiệu quả của Constructor, nhưng về Destructor không mang nhiều tính hữu ích rõ ràng, vì trong thực tế node có thể được tái sử dụng, bị hủy ngay lập tức, bị thu hồi 1 cách chủ động, hoặc trở thành 1 node zombie, nên ở phần Destructor sẽ được sử dụng 1 ví dụ khác.
Ví dụ tạo 1 kiểu dữ liệu mới, lớp Integer (kiểu số nguyên) để thấy được tính hữu ích của Destructor.
- Lớp Integer này tạo ra 1 kiểu dữ liệu mới mà tại thời điểm thực thi, 4 byte dữ liệu của kiểu integer được lưu ngẫu nhiên trên các vùng nhớ để tránh các trường hợp rà soát dữ liệu trên bộ nhớ.
- Lớp Integer này mặc dù được viết dưới dạng hướng đối tượng, nhưng để sử dụng cho tiện lợi, cần viết các phương thức sao cho nó được thao tác thoải mái như đang làm việc với kiểu
int
.- Nên cần thêm các operator overload.
- Nên phải được hủy 1 cách tự nhiên khi ra khỏi tầm vực thay vì phải tự gọi phương thức destroy, đây chính là điểm hữu dụng của destructor.
using BYTE = unsigned char; class Integer { private: BYTE* byte[4]; public: Integer(int value) { BYTE* temp = reinterpret_cast<BYTE*>(&value); for (int i = 0; i < 4; i++) { byte[i] = new BYTE; *byte[i] = temp[i]; } } ~Integer() { for (int i = 0; i < 4; i++) { if (byte[i] != nullptr) delete byte[i]; } } }; int main() { Integer i(1); Integer i(2); // Integer i3 = i1 + i2; return 0; }