Lập trình tổng quát trong C++: Template hàm và Template lớp

C++ nổi bật với khả năng lập trình tổng quát (Generic Programming) và hệ thống template mạnh mẽ, cho phép phát triển mã nguồn có tính tái sử dụng cao và linh hoạt. Bài viết này sẽ đi sâu vào khái niệm lập trình tổng quát trong C++, cùng với hai thành phần cốt lõi: template hàm và template lớp.

1. Tổng quan về Lập trình tổng quát (Generic Programming)

Lập trình tổng quát là một phương pháp xây dựng mã không phụ thuộc vào các kiểu dữ liệu cụ thể, từ đó tối ưu hóa việc tái sử dụng code. Trong C++, triết lý này được hiện thực hóa thông qua các template, cho phép các hàm hoặc lớp làm việc với nhiều kiểu dữ liệu khác nhau.

Ưu điểm nổi bật:

  • Tái sử dụng mã nguồn hiệu quả: Tránh phải viết lại cùng một logic cho từng kiểu dữ liệu riêng biệt, giảm đáng kể sự trùng lặp.
  • Đảm bảo an toàn kiểu dữ liệu: Các template thực hiện kiểm tra kiểu nghiêm ngặt tại thời điểm biên dịch, giúp phát hiện và ngăn chặn lỗi trước khi chương trình thực thi.

Ví dụ minh họa:

#include <iostream>

template<typename KieuDL>
KieuDL tongHaiGiaTri(KieuDL val1, KieuDL val2) {
    return val1 + val2;
}

int main() {
    std::cout << "Tong cua 5 va 10 la: " << tongHaiGiaTri(5, 10) << std::endl;      // Dùng với kiểu int
    std::cout << "Tong cua 3.5f va 4.5f la: " << tongHaiGiaTri(3.5f, 4.5f) << std::endl; // Dùng với kiểu float
    return 0;
}

Trong ví dụ trên, hàm tongHaiGiaTri có thể chấp nhận mọi kiểu dữ liệu miễn là chúng hỗ trợ phép cộng. Đây là một minh chứng rõ ràng cho sức mạnh của lập trình tổng quát.

2. Template hàm (Function Templates)

Template hàm là nền tảng của lập trình template trong C++, cho phép bạn định nghĩa các hàm độc lập với kiểu dữ liệu. Nhờ template hàm, một định nghĩa hàm duy nhất có thể phục vụ nhiều kiểu dữ liệu mà không cần viết các phiên bản riêng biệt.

Cú pháp cơ bản:

template<typename T>
KieuTraVe TenHam(DanhSachThamSo) {
    // Thân hàm
}
  • typename T: Khai báo T là một tham số kiểu dữ liệu.
  • T: Được sử dụng làm kiểu trả về hoặc kiểu của các tham số trong hàm.

Ví dụ:

#include <iostream>
#include <string> // Có thể cần nếu ví dụ sau sử dụng std::string

template<typename Kieu>
Kieu timMax(Kieu giaTri1, Kieu giaTri2) {
    return (giaTri1 > giaTri2) ? giaTri1 : giaTri2;
}

int main() {
    std::cout << "Max cua 100 va 20 la: " << timMax(100, 20) << std::endl;        // int
    std::cout << "Max cua 5.14 va 2.72 la: " << timMax(5.14, 2.72) << std::endl;   // double
    std::cout << "Max cua 'x' va 'y' la: " << timMax('x', 'y') << std::endl;       // char
    return 0;
}

Hàm timMax trên đây có thể so sánh bất kỳ kiểu dữ liệu nào hỗ trợ toán tử >, giúp mã nguồn trở nên linh hoạt và ngắn gọn.

Nạp chồng (Overloading) và Chuyên biệt hóa (Specialization):

  • Nạp chồng template hàm: Template hàm có thể được nạp chồng tương tự như các hàm thông thường, cho phép định nghĩa nhiều phiên bản với các chữ ký tham số khác nhau.
  • Chuyên biệt hóa template: Trong một số trường hợp, bạn có thể cần cung cấp một triển khai độc lập cho một kiểu dữ liệu cụ thể. Đây là mục đích của chuyên biệt hóa template.

Ví dụ chuyên biệt hóa:

#include <cstring> // Cần cho std::strcmp

// Chuyên biệt hóa template timMax cho kiểu const char* để so sánh chuỗi C-style
template<>
const char* timMax<const char*>(const char* chuoi1, const char* chuoi2) {
    return (std::strcmp(chuoi1, chuoi2) > 0) ? chuoi1 : chuoi2;
}

// Để kiểm tra hàm chuyên biệt hóa, bạn có thể thêm đoạn mã này vào hàm main:
/*
int main() {
    const char* str1 = "apple";
    const char* str2 = "banana";
    std::cout << "Max cua \"" << str1 << "\" va \"" << str2 << "\" la: " << timMax(str1, str2) << std::endl;
    return 0;
}
*/

Đoạn mã trên cung cấp một phiên bản chuyên biệt của hàm timMax dành riêng cho kiểu const char*, sử dụng hàm std::strcmp để so sánh các chuỗi ký tự C-style.

3. Template lớp (Class Templates)

Template lớp là một khía cạnh quan trọng khác của lập trình template trong C++. Chúng cho phép bạn xây dựng các lớp tổng quát, có khả năng xử lý nhiều kiểu dữ liệu khác nhau trong cùng một cấu trúc lớp.

Cú pháp định nghĩa:

template<typename T>
class TenLop {
    // Các thành viên dữ liệu và hàm thành viên
};
  • typename T: Chỉ định T là một tham số kiểu cho lớp.
  • T: Có thể được dùng làm kiểu của các biến thành viên hoặc kiểu tham số cho các hàm thành viên.

Ví dụ:

#include <iostream>
#include <string>

template<typename KieuGoi>
class Container {
private:
    KieuGoi item;
public:
    Container(KieuGoi data) : item(data) {}

    KieuGoi layNoiDung() const {
        return item;
    }

    void datNoiDung(KieuGoi newData) {
        this->item = newData;
    }
};

int main() {
    Container<int> soNguyen(456);
    std::cout << "Container so nguyen: " << soNguyen.layNoiDung() << std::endl;

    Container<std::string> chuoiKyTu("Chao mung ban!");
    std::cout << "Container chuoi: " << chuoiKyTu.layNoiDung() << std::endl;

    return 0;
}

Lớp template Container trong ví dụ này có thể lưu trữ và quản lý dữ liệu của nhiều kiểu khác nhau, từ int cho đến std::string, minh họa khả năng tạo ra các cấu trúc dữ liệu tổng quát.

Chuyên biệt hóa và Chuyên biệt hóa một phần (Partial Specialization):

  • Chuyên biệt hóa template lớp: Định nghĩa một phiên bản riêng biệt của lớp template cho một kiểu dữ liệu cụ thể.
  • Chuyên biệt hóa một phần template lớp: Cung cấp một phiên bản chuyên biệt cho một tập hợp con các tham số template, trong khi các tham số khác vẫn giữ tính tổng quát.

Ví dụ chuyên biệt hóa lớp:

#include <iostream> // Cần cho ví dụ sử dụng

// Chuyên biệt hóa lớp Container cho kiểu bool
template<>
class Container<bool> {
private:
    bool flagValue;
public:
    Container(bool value) : flagValue(value) {}
    bool kiemTraTrangThai() const {
        return flagValue;
    }
};

// Để kiểm tra lớp chuyên biệt hóa, bạn có thể thêm đoạn mã này vào hàm main:
/*
int main() {
    Container<bool> booleanContainer(true);
    std::cout << "Gia tri boolean trong Container: " << (booleanContainer.kiemTraTrangThai() ? "True" : "False") << std::endl;
    return 0;
}
*/

Ví dụ trên trình bày một phiên bản chuyên biệt của lớp Container dành riêng cho kiểu bool, với phương thức kiemTraTrangThai() đặc trưng.

Thẻ: C++ Generic Programming Function Templates Class Templates Template Specialization

Đăng vào ngày 6 tháng 11 lúc 03:01