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

Inline Virtual Function ใน C++: เมื่อไหร่ Inline ได้/ไม่ได้

Virtual functions และ inline functions มีเป้าหมายต่างกัน: Virtual สำหรับ runtime polymorphism, Inline สำหรับ compile-time optimization แต่สามารถร่วมกันได้ในบางกรณี

กฎสำคัญ: When Can Virtual Be Inlined?

การเรียกStatic TypeDynamic TypeInline ได้ไหม?
obj.func()Known (Derived)Known (Derived)✅ **ได้** (Compiler รู้ type แน่นอน)
ptr->func()Base*Derived❌ **ไม่ได้** (Runtime dispatch ผ่าน vtable)
ref.func()Base&Derived❌ **ไม่ได้** (Runtime dispatch)
Base::func()BaseBase✅ ได้ (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

  1. Define virtual functions in class body → implicit inline hint
  2. Use final on leaf classes → enables devirtualization
  3. Avoid virtual calls in hot loops → use templates/CRTP สำหรับ static polymorphism
  4. 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

ScenarioOverheadOptimization
Virtual call via pointer/refIndirect call (vtable lookup)Devirtualization, LTO, PGO
Virtual call on objectDirect call (can inline)Full inlining possible
Qualified call Base::func()Direct callAlways inlinable
Final class virtual callDirect 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)** แทน