7.3 การทำงานของ Destructor ในภาษาที่รองรับการทำลายอ็อบเจกต์
7.3 การทำงานของ Destructor ในภาษาที่รองรับการทำลายอ็อบเจกต์
Destructor คือฟังก์ชันพิเศษในโปรแกรมเชิงวัตถุ (Object-Oriented Programming) ซึ่งถูกออกแบบมาเพื่อจัดการกับการทำลายอ็อบเจกต์ (object) เมื่อวัตถุสิ้นสุดการใช้งาน โดยฟังก์ชันนี้จะถูกเรียกใช้อัตโนมัติเมื่ออ็อบเจกต์ออกจากขอบเขตการทำงาน (scope) หรือเมื่อหน่วยความจำของอ็อบเจกต์ถูกปล่อยออกมา (deallocated) เช่น เมื่อเรียกใช้คำสั่ง delete บนอ็อบเจกต์ในภาษา C++ หรืออ็อบเจกต์ที่ไม่ถูกใช้งานอีกในภาษาที่มีการจัดการหน่วยความจำอัตโนมัติ เช่น Python
Destructor ทำหน้าที่สำคัญในการช่วยจัดการหน่วยความจำและทรัพยากรอื่น ๆ ที่อ็อบเจกต์ใช้อยู่ ซึ่งอาจรวมถึงการคืนพื้นที่หน่วยความจำ (memory deallocation) การปิดไฟล์ การยกเลิกการเชื่อมต่อฐานข้อมูล หรือการลบวัตถุที่เกี่ยวข้องกับอ็อบเจกต์นั้น ๆ ซึ่งจะช่วยป้องกันปัญหาการรั่วไหลของหน่วยความจำ (memory leak) ทำให้ระบบทำงานได้อย่างมีประสิทธิภาพมากขึ้น
ลักษณะของ Destructor:
มีชื่อเดียวกับคลาส: Destructor จะมีชื่อเหมือนกับชื่อคลาสและมีเครื่องหมาย ~ นำหน้าในภาษา C++ ส่วนในภาษาอื่น ๆ เช่น Python จะใช้ฟังก์ชัน __del__ เป็น Destructor แทน
ไม่มีพารามิเตอร์หรือค่าที่ส่งกลับ: Destructor ไม่มีพารามิเตอร์และไม่สามารถมีค่าที่ส่งกลับได้ เพราะมีการเรียกใช้โดยอัตโนมัติ และระบบจะรู้จักวัตถุที่จะทำลายโดยไม่ต้องการข้อมูลเพิ่มเติม
1. Destructor ในภาษา C++
ใน C++ Destructor ถูกเรียกใช้เมื่ออ็อบเจกต์ออกจากขอบเขตการทำงาน หรือเมื่อคำสั่ง delete ถูกเรียกใช้ในกรณีที่อ็อบเจกต์ถูกสร้างขึ้นด้วยคำสั่ง new Destructor ใน C++ มีประโยชน์ในการคืนค่าทรัพยากรที่วัตถุใช้ไป เช่น การคืนหน่วยความจำหรือการปิดไฟล์ที่อ็อบเจกต์เปิดใช้งานไว้
ตัวอย่างการใช้งาน Destructor ในภาษา C++:
#include <iostream>
using namespace std;
class FileHandler {
private:
FILE* file;
public:
// Constructor เปิดไฟล์
FileHandler(const char* filename) {
file = fopen(filename, "w");
if (file) {
cout << "File opened successfully." << endl;
} else {
cout << "Failed to open file." << endl;
}
}
// Destructor ปิดไฟล์
~FileHandler() {
if (file) {
fclose(file);
cout << "File closed successfully." << endl;
}
}
};
int main() {
FileHandler fileHandler("example.txt"); // สร้างอ็อบเจกต์ของ FileHandler
// เมื่อ main สิ้นสุด อ็อบเจกต์จะถูกทำลายและ Destructor จะถูกเรียกใช้
return 0;
}
ในโค้ดนี้:
เมื่ออ็อบเจกต์ fileHandler ของคลาส FileHandler ถูกสร้างขึ้น Constructor จะเปิดไฟล์ example.txt
เมื่อฟังก์ชัน main สิ้นสุดลง fileHandler จะถูกทำลาย และ Destructor จะถูกเรียกใช้โดยอัตโนมัติเพื่อปิดไฟล์ ซึ่งเป็นการคืนค่าทรัพยากรที่ไม่จำเป็นต้องใช้อีกต่อไป
2. Destructor ในภาษา Python
ใน Python จะใช้เมธอด __del__() แทน Destructor โดย Python จะใช้การเก็บขยะ (garbage collection) ในการจัดการหน่วยความจำ ดังนั้นเมธอด __del__() จะไม่ถูกเรียกใช้ทันทีที่อ็อบเจกต์ออกจาก scope แต่อาจจะถูกเรียกใช้เมื่ออ็อบเจกต์ไม่มีการอ้างอิง (unreferenced) หรือเมื่อ garbage collector พบว่าอ็อบเจกต์นั้นสามารถถูกทำลายได้
ตัวอย่าง การใช้งาน __del__ ใน Python:
class FileHandler:
def __init__(self, filename):
self.file = open(filename, 'w')
print("File opened successfully.")
def __del__(self):
if self.file:
self.file.close()
print("File closed successfully.")
# สร้างอ็อบเจกต์ FileHandler
file_handler = FileHandler("example.txt")
# ทำการเขียนข้อมูลลงไฟล์
file_handler.file.write("This is a test.")
# เมื่อสิ้นสุดการทำงานของโปรแกรม __del__ จะถูกเรียกใช้และปิดไฟล์
ในโค้ดนี้:
เมื่ออ็อบเจกต์ file_handler ของคลาส FileHandler ถูกสร้างขึ้น ฟังก์ชัน __init__ จะเปิดไฟล์ example.txt
เมื่ออ็อบเจกต์ file_handler ไม่มีการอ้างอิงอีกต่อไป เมธอด __del__ จะถูกเรียกใช้โดย garbage collector เพื่อปิดไฟล์
ข้อควรระวังในการใช้ __del__() ใน Python คือ Python ใช้ garbage collection ดังนั้นเราไม่สามารถควบคุมการเรียกใช้ __del__() ได้โดยตรง และไม่ควรพึ่งพา __del__() สำหรับการจัดการทรัพยากรที่จำเป็นต้องคืนทันที เช่น การปิดการเชื่อมต่อฐานข้อมูล ควรใช้ with statement หรือโค้ดอื่น ๆ ที่จัดการทรัพยากรโดยตรงแทน
ภาษาอื่น ๆ เช่น Java และ C# ที่ใช้ garbage collection ก็จะมีการจัดการหน่วยความจำในลักษณะที่คล้ายคลึงกับ Python โดยไม่มีการเรียกใช้ Destructor แบบตรง ๆ แต่ใช้การเก็บขยะในการทำลายอ็อบเจกต์ที่ไม่ได้ใช้งานอีกต่อไป
ใน Java เราจะใช้ finalize() ซึ่งคล้ายกับ __del__() ใน Python แต่การใช้ finalize() ใน Java ไม่เป็นที่แนะนำอีกต่อไป เพราะ garbage collector จะไม่เรียกใช้ finalize() ทันที และการใช้งานนั้นไม่แน่นอนและส่งผลต่อประสิทธิภาพการทำงาน ปัจจุบันการใช้ try-with-resources จะเป็นวิธีที่เหมาะสมในการจัดการทรัพยากรแทนการพึ่ง finalize()
ตัวอย่าง การใช้งาน try-with-resources ใน Java:
import java.io.*;
class FileHandler {
public static void main(String[] args) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter("example.txt"))) {
writer.write("This is a test.");
System.out.println("File written successfully.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
ในโค้ดนี้:
ใช้ try-with-resources เพื่อให้ไฟล์ example.txt ถูกปิดโดยอัตโนมัติเมื่อสิ้นสุดการทำงานของ try block ซึ่งเป็นวิธีการจัดการทรัพยากรที่ปลอดภัยและแนะนำในการพัฒนาโปรแกรมใน Java
Destructor ใน C++: ถูกเรียกใช้เมื่ออ็อบเจกต์ออกจาก scope หรือถูกลบด้วยคำสั่ง delete ซึ่งช่วยในการคืนค่าทรัพยากรเช่นหน่วยความจำหรือไฟล์
Destructor ใน Python: ใช้เมธอด __del__() ซึ่งจะถูกเรียกใช้เมื่อ garbage collector พบว่าอ็อบเจกต์นั้นไม่ถูกใช้อีกแล้ว อย่างไรก็ตาม ไม่ควรพึ่งพา __del__() เพื่อการจัดการทรัพยากรสำคัญที่ต้องปิดทันที
การจัดการทรัพยากรใน Java: ใช้ try-with-resources แทน finalize() เพราะการเรียก finalize() ไม่แน่นอนและอาจส่งผลกระทบต่อประสิทธิภาพ