Sử Dụng Template Trong C++ Thế Nào (Phần 1)

Trong bài viết này mình sẽ hướng dẫn cách bạn tiếp cận tới một kiến thức khá là quan trọng mà mỗi người học lập trình C++ đều phải biết và áp dụng nó. Đó chính là Template.

1. Template trong C++ là gì?

  • Template (khuôn mẫu) là một từ khóa trong C++, và là một kiểu dữ liệu trừu tượng tổng quát hóa cho các kiểu dữ liệu int, float, double, bool
  • Template trong C++ có 2 loại đó là function template & class template.
  • Template giúp người lập trình định nghĩa tổng quát cho hàm và lớp thay vì phải nạp chồng (

    overloading) cho từng hàm hay phương thức với những kiểu dữ liệu khác nhau.

2. Cách sử dụng template đối với hàm

  • Cú pháp khai báo hàm khuôn mẫu
template <class T>
T someFunction(T arg1, ...)
{
   ...
}

Tại sao chúng ta phải dùng template nhỉ?

Các bạn thử xem ví dụ đối với hàm swapVal()  dưới đây rồi tự rút ra nhận xét cho riêng mình nhé.

void swapVal(int &a , int &b)
{
     int temp;
     temp = a;
     a = b;
     b = temp;
}

Hàm swapVal(int &a , int &b) chỉ áp dụng cho 2 biến kiểu int, do đó muốn đổi 2 biến với các kiểu dữ liệu double, char, string… chúng ta phải overloading nó thêm 1 lần nữa

void swapVal(double &a , double &b)
{
     double temp;
     temp = a;
     a = b;
     b = temp;
}

Qua ví dụ trên chúng ta có thể thấy việc nạp chồng liên tục các hàm nhiều lần như vậy mất nhiều thời và bộ nhớ của máy tính, để tối ưu việc nạp chồng chúng ta sẽ xử lí chúng bằng cách viết một hàm khuôn mẫu swapVal như sau:

template <class val>
void swapVal(val &a , val &b)
{     
     val temp;
     temp = a;
     a = b;
     b = temp;
}

Chúng ta thử chạy xem sao nhé:

#include <bits/stdc++.h>
using namespace std;
template <class val>
void swapVal(val &a , val &b)
{
     val temp;
     temp = a;
     a = b;
     b = temp;
}
int main()
{
    int intX = 2, intY = 3;
    double doubleX = 1.2, doubleY = 3.1;
    string str1 = "Code", str2 = "Learn";
    //gọi hàm swapVal() đối với 2 số nguyên
    swapVal(intX, intY);
    cout << "intX = " << intX << "\tintY = " << intY << endl;
    //gọi hàm swapVal() đối với 2 số thực
    swapVal(doubleX, doubleY);
    cout << "doubleX = " << doubleX << "\tdoubleY = " << doubleY << endl;
    //gọi hàm swapVal() đối với 2 chuỗi
    swapVal(str1, str2);
    cout << "Str1 = " << str1 << "\tstr2 = " << str2 << endl;
    return 0;
}

Sau khi chạy chương trình sẽ có kết quả:

                                       

Qua ví dụ trên chúng ta có thể thấy kiểu dữ liệu của biến định nghĩa chính xác được quyết định ở thời điểm chạy, và chương trình ngắn gọn hơn rất nhiều so với nạp chồng từng hàm swapVal()

Giải thích:

  • Tiền tố khuôn mẫu
    • template <class val>
      • Ở đây class là kiểu hoặc sự phân lớp nên nó không phải là từ class chúng ta hay thấy ở trong lập trình hướng đối tượng.
      • C++ cho phép sử dụng từ khóa “typename” ở vị trí từ khóa class , tuy nhiên chúng ta nên sử dụng từ khóa class trong mọi trường hợp.(Trong phần sau mình sẽ nói rõ khi nào dùng typename , khi nào dùng class)
      • Trong thân định nghĩa hàm, val được sử dụng giống như một kiểu bất kì trùng với kiểu dữ liệu truyền vào.
    • Từ val là tên trượng trưng cho class nên có thể được thay thế bằng bất kì  từ nào khác chúng ta muốn.
  • Định nghĩa khuôn mẫu hàm:
    • Khuôn mẫu hàm swapVal() là một tập hợp các định nghĩa.
    • Khi chúng ta chạy chương trình với đối số truyền vào hàm swapVal() là kiểu int thì khi này trình biên dịch sẽ phát sinh định nghĩa một hàm swapVal(int& a, int& b) mà không phát sinh ra các định nghĩa cho các đối số kiểu float , double hay string. Do vậy chúng ta có thể kết luận được rằng trình biên dịch chỉ phát sinh các định nghĩa khi được yêu cầu với điều kiện ta đã định nghĩa cho tất cả các kiểu.
    • Qua đó ta có thể thấy ưu điểm vượt trội của template trong C++ là chúng ta viết một hàm định nghĩa nó sẽ làm việc cho tất cả các kiểu có thể có.
  • Gọi khuôn mẫu hàm
    • Xét lời gọi hàm swapVal(intX,intY) bộ biên dịch C++ sử dụng khuôn mẫu để khởi tạo định nghĩa hàm cho hai tham số int và tương tự với tất cả các kiểu khác.

Một số lưu ý khi viết hàm template:

  • Một template <class temp> , temp chỉ có tác vụ định nghĩa cho 1 kiểu dữ liệu duy nhất do đó chúng ta không thể gán 1 biến kiểu intdouble cho cùng 1 kiểu dữ liệu temp trong cùng một thời điểm.

Ví dụ:

#include <bits/stdc++.h>
using namespace std;
template <class temp>
void sum(temp intVal , temp doubleVal) 
{
	cout<<int1+double1;
}
int main()
{
	int intVal = 1;
	double doubleVal = 1.1;
	sum(intVal , doubleVal);
 return 0;
 }

Chương trình sẽ báo lỗi như ảnh dưới

           

Để giải quyết vấn đề đó thì ta khai báo thêm một kiểu dữ liệu tổng quát vào như sau:

#include <bits/stdc++.h>
using namespace std;

template <class temp , class temp1>
void sum(temp intVal , temp1 doubleVal) 
{
	cout<<int1+double1;
}

int main()
{
	int intVal = 1;
	double doubleVal = 1.1;
	sum(intVal , doubleVal);
 return 0;
 }

Khi chạy chương trình ta sẽ nhận được kết quả là 2.1, từ đây chúng ta có thể thấy rằng khi ta muốn dùng bao nhiêu kiểu dữ liệu tổng quát thì khai báo bấy nhiêu.

  • Khi lấy temp làm kiểu dữ liệu trả về cho hàm, chúng cũng chỉ nhận kiểu dữ liệu của biến trả về theo kiểu dữ liệu của biến truyền vào mà không trả về một kiểu dữ liệu khác được
#include <bits/stdc++.h>
using namespace std;
template <class temp>
temp thuong(temp x1, temp x2)
{
	return (x1*1.0)/x2;
}
int main()
{
	int x1 = 1 , x2 = 2;
	cout<<thuong(x1,x2);
 	return 0;
 }

Kết quả của chương trình ra 0 bởi vì temp sẽ nhận kiểu dữ liệu theo biến x1 , x2 đã được định nghĩa trước kiểu int vì thế giá trị trả về là 0 mà không thể trả về kết quả như mong muốn là 0.5, do khi trình biên dịch chạy nó sẽ tự động nhận kiểu dữ liệu của biến truyền vào. Vì vậy khi viết khuôn hàm mẫu chúng ta nên cẩn thận trong trường hợp trả về kết quả như này nhé.

Tạm Kết

Vậy là chúng ta đã tìm hiểu được một phần của template trong C++, cụ thể là function template. Qua bài viết này mình mong các bạn nhớ được cú pháp, các dùng của function template và những lưu ý khi viết hàm template.

Mình mong các có thể làm được bài tập nhỏ mình đưa ra sau đây: “Viết một hàm có sử dụng khuôn mẫu nhập vào một mảng có các giá trị có kiểu tùy ý và trả về giá trị có số lần xuất hiện nhiều nhất trong mảng.” (bạn nào cần code thì comment gmail nhé)

Mong các bạn rate và comment ý kiến ở phía dưới để những bài sau mình ra tốt hơn. Cảm ơn các bạn hẹn gặp lại các bạn vào phần tiếp theo Class Template.