Virtual functions และ inline functions มีเป้าหมายต่างกัน: Virtual สำหรับ runtime polymorphism, Inline สำหรับ compile-time optimization แต่สามารถร่วมกันได้ในบางกรณี
กฎสำคัญ: When Can Virtual Be Inlined?
| การเรียก | Static Type | Dynamic Type | Inline ได้ไหม? |
|---|---|---|---|
obj.func() | Known (Derived) | Known (Derived) | ✅ **ได้** (Compiler รู้ type แน่นอน) |
ptr->func() | Base* | Derived | ❌ **ไม่ได้** (Runtime dispatch ผ่าน vtable) |
ref.func() | Base& | Derived | ❌ **ไม่ได้** (Runtime dispatch) |
Base::func() | Base | Base | ✅ ได้ (Qualified call - non-virtual) |
ตัวอย่างโค้ด
#include <iostream>
using namespace std;
class Base {
public:
virtual void func() { // virtual (implicit inline if defined in class)
cout << "Base::func()\n";
}
virtual ~Base() = default;
};
class Derived : public Base {
public:
void func() override { // override
cout << "Derived::func()\n";
}
};
int main() {
Base b;
Derived d;
Base* bp = &d;
Base& br = d;
cout << "--- Object calls (can inline) ---\n";
b.func(); // ✅ Can inline: static type = Base, dynamic = Base
d.func(); // ✅ Can inline: static type = Derived, dynamic = Derived
cout << "\n--- Pointer/Reference calls (cannot inline) ---\n";
bp->func(); // ❌ Cannot inline: virtual dispatch via vtable
br.func(); // ❌ Cannot inline: virtual dispatch via vtable
cout << "\n--- Qualified call (non-virtual) ---\n";
d.Base::func(); // ✅ Can inline: explicitly call Base version
bp->Base::func(); // ✅ Can inline: qualified call
return 0;
}
ผลลัพธ์
--- Object calls (can inline) --- Base::func() Derived::func() --- Pointer/Reference calls (cannot inline) --- Derived::func() Derived::func() --- Qualified call (non-virtual) --- Base::func() Base::func()
การ Optimize ของ Compiler (Devirtualization)
Modern compilers (GCC, Clang, MSVC) สามารถทำ **Devirtualization** ได้เมื่อ:
- Object type ถูกต้องที่ compile-time (เช่น local object, final class)
- Link-time optimization (LTO) เปิดอยู่
- Profile-guided optimization (PGO)
class Base { virtual void foo(); };
class Derived final : public Base { void foo() override; }; // final helps!
void func(Base* ptr) {
ptr->foo(); // May be devirtualized if compiler proves ptr always points to Derived
}
Best Practices
- Define virtual functions in class body → implicit inline hint
- Use
finalon leaf classes → enables devirtualization - Avoid virtual calls in hot loops → use templates/CRTP สำหรับ static polymorphism
- Consider NVI pattern → public non-virtual calls private virtual
NVI Pattern (Non-Virtual Interface)
class Base {
public:
// Non-virtual public interface - CAN be inlined
void process(int data) {
preProcess(data); // Inlineable
doProcess(data); // Virtual call (cannot inline via pointer)
postProcess(data); // Inlineable
}
virtual ~Base() = default;
private:
virtual void doProcess(int data) = 0; // Pure virtual implementation
void preProcess(int) { /* common */ }
void postProcess(int) { /* common */ }
};
Performance Implications
| Scenario | Overhead | Optimization |
|---|---|---|
| Virtual call via pointer/ref | Indirect call (vtable lookup) | Devirtualization, LTO, PGO |
| Virtual call on object | Direct call (can inline) | Full inlining possible |
Qualified call Base::func() | Direct call | Always inlinable |
| Final class virtual call | Direct call (can inline) | Devirtualization guaranteed |
สรุป
- Virtual functions **สามารถ inline ได้** เฉพาะเมื่อ **static type = dynamic type** (เรียกผ่าน object โดยตรง)
- เรียกผ่าน pointer/reference **ไม่สามารถ inline ได้** (ต้องใช้ vtable dispatch)
- Modern compilers ทำ **devirtualization** ได้ดีขึ้นเรื่อยๆ (ใช้
final, LTO, PGO) - สำหรับ performance-critical code: พิจารณา **static polymorphism (templates/CRTP)** แทน