Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C++

กฎของสาม (Rule of Three) ใน C++

กฎของสาม (Rule of Three) เป็นหนึ่งในกฎของ C++ ภายใต้กฎทั่วไปสำหรับการสร้างโค้ดที่ปลอดภัยจากข้อยกเว้น (exception safe) กฎเหล่านี้ระบุว่าสมาชิกเริ่มต้น (default members) ของคลาสควรถูกใช้อย่างไรเพื่อปฏิบัติที่ปลอดภัยจากข้อยกเว้น

กฎของสามยังเรียกว่า Law of Big Three หรือ The Big Three และระบุสำหรับคลาสว่า หากคลาสกำหนดฟังก์ชันใดฟังก์ชันหนึ่งในสามฟังก์ชันต่อไปนี้ ควรจะกำหนดทั้งสามอย่างอย่างชัดเจน (explicitly):

  • Destructor (ตัวทำลาย)
  • Copy Constructor (คอนสตรัคเตอร์คัดลอก)
  • Copy Assignment Constructor (คอนสตรัคเตอร์กำหนดค่าคัดลอก)

ทั้งสามนี้เป็นฟังก์ชันสมาชิกพิเศษของคลาส หากไม่มีฟังก์ชันใดถูกกำหนดอย่างชัดเจนโดยโปรแกรมเมอร์ คอมไพเลอร์จะให้เวอร์ชันโดยนัย (implicit versions) หากมีการกำหนดอย่างชัดเจนเพียงหนึ่งในสาม เวอร์ชันโดยนัยสำหรับอีกสองตัวจะไม่เหมาะสมและต้องกำหนดใหม่

สิ่งนี้เกิดขึ้นเพราะคอนสตรัคเตอร์และออเปอเรเตอร์กำหนดค่าที่สร้างโดยนัยจะทำการ shallow copy (คัดลอกตื้น) สมาชิกข้อมูล เราต้องการ deep copy (คัดลอกลึก) เมื่อคลาสมี pointer ชี้ไปยังทรัพยากรที่จัดสรรแบบไดนามิก

Destructor เริ่มต้นจะลบอ็อบเจกต์ที่ไม่ได้ใช้ เมื่อไม่มี copy constructor กำหนดไว้ Destructor จะทำงานสองครั้ง ครั้งหนึ่งสำหรับอ็อบเจกต์ที่มีการคัดลอก และอีกครั้งสำหรับอ็อบเจกต์ที่ข้อมูลถูกคัดลอกมาจากนั้น เพื่อหลีกเลี่ยงเรื่องนี้ การกำหนดอย่างชัดเจนจึงเป็นสิ่งจำเป็น

มาดูตัวอย่างที่ไม่มี copy constructor และ copy assignment operator แต่มี destructor:

ตัวอย่าง

#include <stdio.h>
class Numbers {
private:
    int num;
    int* ptr;
public:
    Numbers(int n, int* p) // copy constructor {
        num = n;
        ptr = new int[num];
    }
    ~Numbers() // destructor {
        delete ptr;
        ptr = NULL;
    }
};

int main() {
    int arr[4] = { 11, 22, 33, 44 };
    Numbers Num1(4, arr);
    // นี่สร้างปัญหา
    Numbers Num2(Num1);
    return 0;
}

ผลลัพธ์

*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001f46c20 *** Aborted
  • สิ่งนี้เกิดขึ้นเพราะ destructor ถูกเรียกสองครั้งเมื่อโปรแกรมออกจากขอบเขต (scope) ครั้งแรกลบ Num1 แล้วครั้งที่สองสำหรับ Num2 Default copy constructor สร้างคัดลอกของ pointer ptr แต่ไม่ได้จัดสรรหน่วยความจำสำหรับมัน ดังนั้นเมื่อ Num1 ถูกลบ pointer ptr ต่อไปทำให้โปรแกรมล้มเหลว (crash)