Skip to content

🎭 Virtual Functions — Hàm ảo

virtual là chìa khóa để đạt được runtime polymorphism — gọi đúng method dựa trên actual type của object, không phải declared type.

Vấn đề: Method Hiding

cpp
#include <iostream>

class Animal {
public:
    void speak() {
        std::cout << "Some sound\n";
    }
};

class Dog : public Animal {
public:
    void speak() {
        std::cout << "Woof!\n";
    }
};

int main() {
    Dog myDog;
    Animal* ptr = &myDog;  // Base pointer to derived object
    
    ptr->speak();  // ❌ Output: "Some sound" — KHÔNG phải "Woof!"
}

Vấn đề: Compiler quyết định gọi method nào dựa trên declared type (Animal*), không phải actual type (Dog).


Giải pháp: virtual Keyword

cpp
#include <iostream>

class Animal {
public:
    virtual void speak() {  // ✅ Thêm virtual
        std::cout << "Some sound\n";
    }
};

class Dog : public Animal {
public:
    void speak() override {  // ✅ override (C++11)
        std::cout << "Woof!\n";
    }
};

class Cat : public Animal {
public:
    void speak() override {
        std::cout << "Meow!\n";
    }
};

int main() {
    Dog myDog;
    Cat myCat;
    
    Animal* ptr1 = &myDog;
    Animal* ptr2 = &myCat;
    
    ptr1->speak();  // ✅ "Woof!" — runtime dispatch
    ptr2->speak();  // ✅ "Meow!" — runtime dispatch
}

override Keyword (C++11)

override không bắt buộc, nhưng HIGHLY RECOMMENDED:

cpp
class Animal {
public:
    virtual void speak() const { }
};

class Dog : public Animal {
public:
    // ❌ Bug: quên 'const' — tạo method mới, không override!
    void speak() { }
    
    // ✅ Với override, compiler check cho bạn
    void speak() override { }  // ❌ Error: no matching base method
    
    // ✅ Correct
    void speak() const override { }
};

📌 HPN Standard

LUÔN dùng override khi override virtual method. Compiler sẽ bắt lỗi nếu bạn không thực sự override gì cả.


final Keyword (C++11)

Ngăn Override thêm

cpp
class Animal {
public:
    virtual void speak() { }
};

class Dog : public Animal {
public:
    void speak() final {  // ✅ Không ai được override nữa
        std::cout << "Woof!\n";
    }
};

class Bulldog : public Dog {
public:
    // ❌ Error: speak is marked final
    // void speak() override { }
};

Ngăn Inheritance

cpp
class Singleton final {  // ✅ Không ai được kế thừa class này
public:
    static Singleton& instance();
};

// ❌ Error: Singleton is final
// class MySingleton : public Singleton { };

Virtual Destructor — CRITICAL!

Khi delete object qua base pointer, PHẢI có virtual destructor:

cpp
#include <iostream>

class Base {
public:
    Base() { std::cout << "Base constructed\n"; }
    ~Base() { std::cout << "Base destroyed\n"; }  // ❌ Non-virtual
};

class Derived : public Base {
    int* data_;
public:
    Derived() : data_(new int[100]) {
        std::cout << "Derived constructed\n";
    }
    ~Derived() {
        delete[] data_;
        std::cout << "Derived destroyed\n";
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;  // ❌ MEMORY LEAK! Derived destructor không được gọi!
}

Output:

Base constructed
Derived constructed
Base destroyed        ← Derived::~Derived() KHÔNG được gọi!

Fix: Virtual Destructor

cpp
class Base {
public:
    virtual ~Base() {  // ✅ Virtual destructor
        std::cout << "Base destroyed\n";
    }
};

class Derived : public Base {
    int* data_;
public:
    ~Derived() override {  // ✅ override
        delete[] data_;
        std::cout << "Derived destroyed\n";
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;  // ✅ Cả Derived và Base destructor đều được gọi!
}

Output:

Derived destroyed
Base destroyed

⚠️ RULE OF THUMB

Nếu class có ít nhất một virtual method, destructor PHẢI là virtual!


Cách Virtual Functions hoạt động: vtable

┌─────────────────────────────────────────────────────────────────┐
│                    HOW VIRTUAL WORKS                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Mỗi class có virtual function → Có một VTABLE                 │
│                                                                 │
│  Animal vtable:              Dog vtable:                        │
│  ┌────────────────┐          ┌────────────────┐                 │
│  │ speak() ────────┼───→     │ speak() ────────┼───→ Dog::speak │
│  └────────────────┘          └────────────────┘                 │
│         │                           │                           │
│         ▼                           ▼                           │
│  Animal::speak()              Dog::speak()                      │
│                                                                 │
│  Object Layout:                                                 │
│  ┌─────────────────────┐                                        │
│  │ vptr ─────────────────┼───→ vtable                           │
│  ├─────────────────────┤                                        │
│  │ member variables    │                                        │
│  └─────────────────────┘                                        │
│                                                                 │
│  Runtime: ptr->speak() → vptr → vtable[0] → correct speak()    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Chi phí của Virtual:

  • Mỗi object có thêm vptr (8 bytes on 64-bit)
  • Indirect function call (cache miss có thể xảy ra)
  • Nhưng đây là giá rất nhỏ cho flexibility!

Covariant Return Types

Virtual function có thể return derived type:

cpp
class Animal {
public:
    virtual Animal* clone() const {
        return new Animal(*this);
    }
};

class Dog : public Animal {
public:
    Dog* clone() const override {  // ✅ Return Dog*, không phải Animal*
        return new Dog(*this);
    }
};

📚 Tổng kết

KeywordMeaning
virtualEnable runtime dispatch
overrideVerify override at compile time
finalPrevent further override
Virtual destructorRequired if deleting via base pointer
vtableRuntime lookup table cho virtual methods

➡️ Tiếp theo

Tiếp theo: Polymorphism — Sức mạnh của runtime polymorphism.