Giao diện
🎭 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
| Keyword | Meaning |
|---|---|
virtual | Enable runtime dispatch |
override | Verify override at compile time |
final | Prevent further override |
| Virtual destructor | Required if deleting via base pointer |
| vtable | Runtime lookup table cho virtual methods |
➡️ Tiếp theo
Tiếp theo: Polymorphism — Sức mạnh của runtime polymorphism.
🧠 Quiz
Câu 1: Đoạn code sau in ra gì?
cpp
class Animal {
public:
void speak() { std::cout << "Animal"; }
};
class Dog : public Animal {
public:
void speak() { std::cout << "Dog"; }
};
int main() {
Dog d;
Animal* p = &d;
p->speak();
}- [x] A)
Animal— vìspeak()không phải virtual, compiler dùng declared type - [ ] B)
Dog— vì object thực tế làDog - [ ] C) Compile error
- [ ] D) Undefined behavior
💡 Giải thích: Khi method không có
virtual, compiler quyết định gọi method nào dựa trên declared type của pointer (Animal*), không phải actual type (Dog). Đây gọi là method hiding. ThêmvirtualvàoAnimal::speak()để có runtime dispatch đúng.
Câu 2: Keyword final trên một virtual function có tác dụng gì?
- [ ] A) Biến method thành pure virtual
- [ ] B) Tăng hiệu suất gọi hàm
- [x] C) Ngăn không cho derived class override method đó
- [ ] D) Tự động gọi base class version trước
💡 Giải thích:
finaltrên virtual function ngăn bất kỳ derived class nào override method đó. Compiler sẽ báo lỗi nếu cố override.finalcũng có thể dùng trên class (class X final) để ngăn inheritance hoàn toàn. Ngoài ra, compiler có thể devirtualize call tớifinalmethod để tăng hiệu suất.
Câu 3: Điều gì xảy ra khi delete derived object qua base pointer mà base destructor KHÔNG virtual?
- [ ] A) Cả Base và Derived destructor đều được gọi
- [ ] B) Compile error
- [x] C) Chỉ Base destructor được gọi — Derived destructor bị bỏ qua, gây memory leak
- [ ] D) Chỉ Derived destructor được gọi
💡 Giải thích: Nếu base destructor không phải
virtual, khideletequa base pointer, chỉ có base destructor chạy. Derived destructor bị bỏ qua → resource của derived class không được giải phóng → memory leak. Quy tắc: nếu class có ít nhất một virtual method, destructor phải virtual.