ได้** Virtual functions ใน C++ สามารถมี default parameters ได้ แต่มีพฤติกรรมพิเศษที่ต้องระวัง
กฎสำคัญ: Default Parameters ผูกที่ Compile-time (Static Binding)
- Default arguments **ไม่ได้เป็นส่วนหนึ่งของ function signature**
- Default value ถูก.resolve() ที่ **compile-time** ตาม **static type** ของ pointer/reference
- Function ที่ถูกเรียก (dynamic dispatch) ถูกตัดสินใจที่ **runtime** ตาม **dynamic type**
ตัวอย่างแสดงพฤติกรรม
#include <iostream>
using namespace std;
class Base {
public:
virtual void func(int x = 10) { // Default = 10
cout << "Base::func(" << x << ")\n";
}
};
class Derived : public Base {
public:
void func(int x = 20) override { // Default = 20 (ต่างจาก Base!)
cout << "Derived::func(" << x << ")\n";
}
};
int main() {
Derived d;
Base* bp = &d;
Base& br = d;
cout << "--- Direct call (static type Derived) ---\n";
d.func(); // Derived::func(20) - ใช้ default ของ Derived
cout << "\n--- Via Base pointer (static type Base) ---\n";
bp->func(); // Derived::func(10) - เรียก Derived แต่ใช้ default ของ Base!
cout << "\n--- Via Base reference (static type Base) ---\n";
br.func(); // Derived::func(10) - เรียก Derived แต่ใช้ default ของ Base!
cout << "\n--- Explicit arguments ---\n";
bp->func(99); // Derived::func(99) - explicit argument
d.func(99); // Derived::func(99)
return 0;
}
ผลลัพธ์
--- Direct call (static type Derived) --- Derived::func(20) --- Via Base pointer (static type Base) --- Derived::func(10) --- Via Base reference (static type Base) --- Derived::func(10) --- Explicit arguments --- Derived::func(99) Derived::func(99)
การวิเคราะห์
| Call | Static Type | Dynamic Type | Function Called | Default Used |
|---|---|---|---|---|
d.func() | Derived | Derived | Derived::func | 20 (Derived) |
bp->func() | Base | Derived | Derived::func | 10 (Base) |
br.func() | Base | Derived | Derived::func | 10 (Base) |
bp->func(99) | Base | Derived | Derived::func | 99 (explicit) |
Best Practice: หลีกเลี่ยง Default Parameters ใน Virtual Functions
// ❌ Bad: Default parameters in virtual functions
class Base {
virtual void foo(int x = 10);
};
class Derived : public Base {
void foo(int x = 20) override; // Confusing!
};
// ✅ Good: ใช้ overload แทน
class Base {
public:
virtual void foo(int x) { /* implementation */ }
void foo() { foo(10); } // Non-virtual wrapper with default
};
class Derived : public Base {
public:
void foo(int x) override { /* implementation */ }
void foo() { foo(20); } // Own default
};
อีกแนวทาง: Non-Virtual Interface (NVI) Pattern
class Base {
public:
// Public non-virtual interface
void doOperation(int x = 10) {
// Pre-processing...
doOperationImpl(x); // Call private virtual
// Post-processing...
}
private:
virtual void doOperationImpl(int x) = 0; // Pure virtual implementation
};
class Derived : public Base {
private:
void doOperationImpl(int x) override {
// Derived-specific implementation
}
};
สรุป
- Virtual functions **สามารถ** มี default parameters ได้
- แต่ **default value ถูกผูกที่ static type** (compile-time)
- Function body ที่รันคือของ dynamic type (runtime)
- แนะนำ: หลีกเลี่ยง default parameters ใน virtual functions ใช้ NVI pattern หรือ overload แทน