Skip to content

🔮 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

TypeMechanismKhi quyết định?
Compile-timeFunction overloading, TemplatesCompile time
RuntimeVirtual functionsRuntime
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

ConceptKey Takeaway
PolymorphismMột interface, nhiều implementations
Runtime dispatchQua virtual + vptr/vtable
Pointer/ReferenceBắt buộc cho polymorphism (tránh slicing)
Smart pointersModern C++ cách quản lý polymorphic objects
dynamic_castSafe downcasting (với RTTI)
OCPOpen for extension, closed for modification

➡️ Tiếp theo

Tiếp theo: Abstract Classes — Pure virtual và interface design.