Virtual base class ใช้แก้ปัญหา **Diamond Problem** (Diamond Inheritance) ที่ derived class ได้รับ base class มาหลายคอผ่านการสืบทอดหลายเส้นทาง
ปัญหา Diamond Inheritance
Base
/ \
D1 D2
\ /
Derived
โดยไม่มี virtual inheritance: Derived จะมี **สองคอ** ของ Base (หนึ่งจาก D1, หนึ่งจาก D2) → ambiguity
ตัวอย่างปัญหา (ไม่ใช้ virtual)
#include <iostream>
using namespace std;
class Base {
public:
int value = 10;
};
class D1 : public Base { // Non-virtual
public:
int d1 = 20;
};
class D2 : public Base { // Non-virtual
public:
int d2 = 30;
};
class Derived : public D1, public D2 {
public:
int d3 = 40;
};
int main() {
Derived obj;
// obj.value = 100; // ❌ COMPILE ERROR: ambiguous!
// obj.value = 200; // ❌ ไม่รู้ว่า value ของ D1::Base หรือ D2::Base
// ต้องระบุ path ชัดเจน:
obj.D1::value = 100;
obj.D2::value = 200;
cout << "D1::value = " << obj.D1::value << endl;
cout << "D2::value = " << obj.D2::value << endl;
cout << "d1 = " << obj.d1 << endl;
cout << "d2 = " << obj.d2 << endl;
cout << "d3 = " << obj.d3 << endl;
return 0;
}
ผลลัพธ์ (ไม่ใช้ virtual)
D1::value = 100 D2::value = 200 d1 = 20 d2 = 30 d3 = 40
แก้ไขด้วย Virtual Base Class
class Base {
public:
int value = 10;
};
class D1 : virtual public Base { // เพิ่ม virtual
public:
int d1 = 20;
};
class D2 : virtual public Base { // เพิ่ม virtual
public:
int d2 = 30;
};
class Derived : public D1, public D2 {
public:
int d3 = 40;
};
int main() {
Derived obj;
obj.value = 100; // ✅ OK: มี Base เพียง 1 คอ
obj.value = 200; // เขียนทับค่าเดิม
cout << "value = " << obj.value << endl; // 200
cout << "d1 = " << obj.d1 << endl;
cout << "d2 = " << obj.d2 << endl;
cout << "d3 = " << obj.d3 << endl;
return 0;
}
ผลลัพธ์ (ใช้ virtual)
value = 200 d1 = 20 d2 = 30 d3 = 40
หลักการทำงานของ Virtual Base Class
- Compiler รับประกันว่ามี **เพียงหนึ่ง subobject** ของ virtual base class ใน complete object
- Derived class (most derived) **รับผิดชอบ initialize virtual base** โดยตรง
- Virtual base ถูกสร้าง **ก่อน** non-virtual bases
Constructor Initialization Order
class Base {
public:
Base(int v) : value(v) { cout << "Base(" << v << ")\n"; }
int value;
};
class D1 : virtual public Base {
public:
D1(int v) : Base(v * 10) { cout << "D1\n"; }
};
class D2 : virtual public Base {
public:
D2(int v) : Base(v * 100) { cout << "D2\n"; }
};
class Derived : public D1, public D2 {
public:
// Derived ต้อง initialize Base เอง (ignore D1, D2 init of Base)
Derived() : Base(999), D1(1), D2(2) {
cout << "Derived\n";
}
};
// Output:
// Base(999)
// D1
// D2
// Derived
ข้อควรระวัง
- Performance: Virtual base ต้องการ pointer/offset เพิ่มเติม (space/time overhead)
- Complexity: Initialization order ซับซ้อนกว่า
- Design: มักบ่งชี้ design flaw - ควรพิจารณา composition แทน inheritance
- Pure virtual destructor: Virtual base มักควรมี virtual destructor
สรุป
Virtual base class แก้ปัญหา diamond inheritance โดยบังคับให้มี base subobject เพียงหนึ่งคอ ใช้คำสำคัญ virtual ในการสืบทอดระดับกลาง (class D1 : virtual public Base) และ most-derived class ต้อง initialize virtual base เอง