Mộc Viên's Blog Mộc Viên's Blog
Giải thích khái niệm CRTP (Curiously Recurring Template Pattern) và cách nó được sử dụng trong C++.

Giải thích khái niệm CRTP (Curiously Recurring Template Pattern) và cách nó được sử dụng trong C++.

Ngày đăng:

Giải thích khái niệm CRTP (Curiously Recurring Template Pattern) và cách nó được sử dụng trong C++.

CRTP (Curiously Recurring Template Pattern) là gì?

CRTP (Curiously Recurring Template Pattern) là một kỹ thuật lập trình trong C++ mà một lớp con kế thừa từ một lớp cha, trong đó lớp cha lại sử dụng lớp con làm tham số template. Cách tiếp cận này thường được sử dụng để thực hiện static polymorphism (đa hình tĩnh), giúp tối ưu hiệu suất bằng cách thay thế dynamic polymorphism (đa hình động) sử dụng con trỏ ảo (virtual).

Cấu trúc cơ bản của CRTP:

template <typename Derived>
class Base {
public:
    void interface() {
        static_cast<Derived*>(this)->implementation();
    }
};

class Derived : public Base<Derived> {
public:
    void implementation() {
        std::cout << "Derived implementation\n";
    }
};

int main() {
    Derived d;
    d.interface(); // Gọi Derived::implementation()
}

Trong đoạn code trên:

  • Lớp Base<Derived> chứa một phương thức interface() gọi implementation() của lớp con (Derived) bằng cách ép kiểu static_cast<Derived*>(this).
  • Lớp Derived kế thừa từ Base<Derived> và cung cấp triển khai cụ thể cho implementation().
  • Khi gọi d.interface(), nó thực sự gọi Derived::implementation(), mà không cần đến bảng vtable như trong cơ chế đa hình động.

Lợi ích của CRTP

  1. Đa hình tĩnh (Static Polymorphism)
    • CRTP cho phép đa hình mà không cần sử dụng con trỏ ảo (virtual), giúp tối ưu hiệu suất vì tránh được overhead của vtable.
  2. Code Reuse (Tái sử dụng mã)
    • CRTP có thể được dùng để tạo các mô hình chung mà lớp con có thể kế thừa, giúp tránh lặp lại code.
  3. Compile-time Polymorphism (Đa hình thời gian biên dịch)
    • Vì tất cả các phương thức được xác định tại thời gian biên dịch, trình biên dịch có thể tối ưu mã tốt hơn.

Giải thích sự khác biệt giữa std::async và std::thread.
Cả std::async và std::thread trong C++ đều được sử dụng để thực thi tác vụ bất đồng bộ (concurrent execution), nhưng chúng có sự khác biệt quan trọng trong cách quản lý luồng và xử lý kết quả. 1. std::thread – Luồng thấp cấp (Low-Level Thread) * std::thread

Ứng dụng của CRTP

1. Tự động cung cấp phương thức interface cho lớp con

CRTP có thể dùng để tạo một interface mà lớp con phải tuân theo:

template <typename Derived>
class Printable {
public:
    void print() {
        static_cast<Derived*>(this)->printImpl();
    }
};

class MyClass : public Printable<MyClass> {
public:
    void printImpl() {
        std::cout << "Printing MyClass\n";
    }
};

int main() {
    MyClass obj;
    obj.print(); // Gọi MyClass::printImpl()
}

2. Tạo Counter cho từng lớp con

CRTP có thể được sử dụng để đếm số lượng instance của mỗi lớp con:

template <typename Derived>
class Counter {
private:
    static int count;
public:
    Counter() { ++count; }
    ~Counter() { --count; }

    static int getCount() { return count; }
};

template <typename Derived>
int Counter<Derived>::count = 0;

class A : public Counter<A> {};
class B : public Counter<B> {};

int main() {
    A a1, a2;
    B b1;

    std::cout << "A count: " << A::getCount() << "\n"; // 2
    std::cout << "B count: " << B::getCount() << "\n"; // 1
}
  • Ở đây, Counter<A>Counter<B> có bộ đếm riêng biệt vì AB là các lớp con khác nhau.

3. Mixin Pattern (Thêm chức năng vào lớp con)

CRTP có thể được sử dụng như Mixin để thêm chức năng vào lớp mà không cần đa kế thừa.

Ví dụ: Thêm logging vào một lớp:

template <typename Derived>
class Logger {
public:
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << std::endl;
    }
};

class Application : public Logger<Application> {
public:
    void run() {
        log("Application is running");
    }
};

int main() {
    Application app;
    app.run();
}
  • Ở đây, lớp Application kế thừa Logger<Application>, nhờ đó nó có thể sử dụng phương thức log() mà không cần tạo thêm biến logger.

So sánh CRTP với Đa hình động (virtual)

Đặc điểm CRTP (Static Polymorphism) Virtual Function (Dynamic Polymorphism)
Cách triển khai Template Dùng con trỏ ảo (virtual)
Overhead Không cần vtable Cần vtable
Tốc độ thực thi Nhanh hơn Chậm hơn
Linh hoạt Giới hạn ở compile-time Linh hoạt hơn ở runtime

Tổng kết

  • CRTP là một mẫu thiết kế giúp đạt được đa hình tĩnh, thay thế con trỏ ảo (virtual).
  • Được sử dụng để tái sử dụng mã, thêm chức năng (Mixin), đếm số lượng đối tượng, v.v.
  • Tối ưu hiệu suất hơn so với đa hình động (virtual), nhưng không linh hoạt bằng vì các kiểu phải được biết tại thời gian biên dịch.

Nếu bạn muốn xây dựng một hệ thống hiệu suất cao mà vẫn tận dụng tính kế thừa, CRTP là một lựa chọn mạnh mẽ trong C++. 🚀


Gần đây