Giao diện
🔮 Polymorphism — Đa hình
Polymorphism nghĩa là "nhiều hình dạng". Một interface, nhiều implementations — code linh hoạt và dễ mở rộng.
Compile-time vs Runtime Polymorphism
| Type | Mechanism | Khi quyết định? |
|---|---|---|
| Compile-time | Function overloading, Templates | Compile time |
| Runtime | Virtual functions | Runtime |
cpp
// Compile-time (Static)
void print(int x) { std::cout << x; }
void print(double x) { std::cout << x; } // Overloaded
// Runtime (Dynamic)
class Shape {
public:
virtual double area() const = 0;
};Sức mạnh của Runtime Polymorphism
cpp
#include <iostream>
#include <vector>
#include <memory>
class Shape {
public:
virtual double area() const = 0; // Pure virtual
virtual void draw() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
double radius_;
public:
explicit Circle(double r) : radius_(r) {}
double area() const override {
return 3.14159 * radius_ * radius_;
}
void draw() const override {
std::cout << "Drawing Circle with radius " << radius_ << std::endl;
}
};
class Rectangle : public Shape {
double width_, height_;
public:
Rectangle(double w, double h) : width_(w), height_(h) {}
double area() const override {
return width_ * height_;
}
void draw() const override {
std::cout << "Drawing Rectangle " << width_ << "x" << height_ << std::endl;
}
};
class Triangle : public Shape {
double base_, height_;
public:
Triangle(double b, double h) : base_(b), height_(h) {}
double area() const override {
return 0.5 * base_ * height_;
}
void draw() const override {
std::cout << "Drawing Triangle with base " << base_ << std::endl;
}
};
// ✅ Function works with ANY shape!
void processShape(const Shape& shape) {
shape.draw();
std::cout << "Area: " << shape.area() << std::endl;
}
int main() {
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(5.0));
shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0));
shapes.push_back(std::make_unique<Triangle>(3.0, 4.0));
// ✅ Polymorphic loop — không cần biết type cụ thể!
for (const auto& shape : shapes) {
processShape(*shape);
std::cout << "---\n";
}
return 0;
}Output:
Drawing Circle with radius 5
Area: 78.5398
---
Drawing Rectangle 4x6
Area: 24
---
Drawing Triangle with base 3
Area: 6
---Open-Closed Principle
Polymorphism giúp đạt được OCP (Open-Closed Principle):
"Software entities should be open for extension, but closed for modification."
cpp
// ❌ WITHOUT polymorphism — phải sửa mỗi khi thêm shape mới
double calculateArea(Shape* shape) {
if (shape->type == "circle") {
return 3.14 * shape->radius * shape->radius;
} else if (shape->type == "rectangle") {
return shape->width * shape->height;
} else if (shape->type == "triangle") { // Phải thêm mỗi lần!
return 0.5 * shape->base * shape->height;
}
// else if ... forever!
}
// ✅ WITH polymorphism — chỉ thêm class mới, không sửa gì
double calculateArea(const Shape& shape) {
return shape.area(); // Tự gọi đúng method!
}Polymorphism với Pointers và References
Pointer to Base
cpp
Shape* shape = new Circle(5.0);
shape->draw(); // ✅ Gọi Circle::draw()
delete shape;Reference to Base
cpp
void render(const Shape& s) {
s.draw(); // ✅ Polymorphic
}
Circle c(5.0);
render(c); // Automatic binding⚠️ By Value — SLICING!
cpp
void badRender(Shape s) { // ❌ By value!
s.draw(); // Chỉ gọi Shape::draw, không phải derived!
}
// Object Slicing: Derived phần bị "cắt" khi copy vào Base⚠️ Object Slicing
Khi pass derived object by value vào function nhận base type, derived part bị sliced off. Luôn dùng pointer hoặc reference cho polymorphism!
Smart Pointers với Polymorphism
cpp
#include <memory>
#include <vector>
class Animal {
public:
virtual void speak() const = 0;
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void speak() const override { std::cout << "Woof!\n"; }
};
class Cat : public Animal {
public:
void speak() const override { std::cout << "Meow!\n"; }
};
int main() {
// ✅ Modern C++ — dùng smart pointers
std::vector<std::unique_ptr<Animal>> zoo;
zoo.push_back(std::make_unique<Dog>());
zoo.push_back(std::make_unique<Cat>());
zoo.push_back(std::make_unique<Dog>());
for (const auto& animal : zoo) {
animal->speak();
}
// Tự động cleanup — no memory leaks!
}Dynamic Cast — Safe Downcasting
cpp
#include <iostream>
class Animal {
public:
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void bark() { std::cout << "Woof!\n"; }
};
class Cat : public Animal {
public:
void meow() { std::cout << "Meow!\n"; }
};
void handleAnimal(Animal* animal) {
// dynamic_cast trả về nullptr nếu cast thất bại
if (Dog* dog = dynamic_cast<Dog*>(animal)) {
dog->bark(); // ✅ Safe — chỉ nếu thực sự là Dog
} else if (Cat* cat = dynamic_cast<Cat*>(animal)) {
cat->meow();
} else {
std::cout << "Unknown animal\n";
}
}
int main() {
Dog d;
Cat c;
handleAnimal(&d); // Woof!
handleAnimal(&c); // Meow!
}📌 Khi nào dùng dynamic_cast?
- Khi cần kiểm tra type cụ thể của polymorphic object
- Trong framework/plugin systems
- Tránh lạm dụng — nếu cần cast nhiều, có thể design sai
typeid và RTTI
cpp
#include <typeinfo>
#include <iostream>
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base { };
int main() {
Base* ptr = new Derived();
std::cout << typeid(*ptr).name() << std::endl; // "Derived" (mangled)
if (typeid(*ptr) == typeid(Derived)) {
std::cout << "It's a Derived!\n";
}
delete ptr;
}📚 Tổng kết
| Concept | Key Takeaway |
|---|---|
| Polymorphism | Một interface, nhiều implementations |
| Runtime dispatch | Qua virtual + vptr/vtable |
| Pointer/Reference | Bắt buộc cho polymorphism (tránh slicing) |
| Smart pointers | Modern C++ cách quản lý polymorphic objects |
| dynamic_cast | Safe downcasting (với RTTI) |
| OCP | Open for extension, closed for modification |
➡️ Tiếp theo
Tiếp theo: Abstract Classes — Pure virtual và interface design.