Templates trong C++

Trong bài này, bạn sẽ được học về templates trong C++. Bạn sẽ được học cách sử dụng sức mạnh của templates trong lập trình khái quát.

Templates là các tính năng mạnh mẽ của C++ cho phép bạn viết các chương trình khái quát. Nói một cách đơn giản, bạn có thể tạo một hàm duy nhất hoặc một lớp để làm việc với các dạng dữ liệu khác nhau sử dụng templates.

Templates thường được sử dụng trong các dự án có lượng mã nguồn lớn nhằm tăng cường tính tái sử dụng mã nguồn cũng như tính khả chuyển của chương trình.

Định nghĩa của templates có thể được sử dụng theo hai cách khác nhau:

  • Templates cho hàm
  • Templates cho lớp

Templates cho hàm

Một template cho hàm hoạt động gần giống với hàm bình thường, chỉ với một điểm khác biệt cốt lõi.

Một template hàm có thể làm việc với rất nhiều kiểu dữ liệu cùng một lúc, nhưng một hàm chỉ có thể làm việc với một kiểu dữ liệu.

Thông thường, nếu bạn cần thực hiện các thao tác tương đồng trên hai hoặc nhiều hơn các kiểu dữ liệu, bạn sẽ sử dụng nạp chồng hàm để tạo ra hai hàm với khai báo mong muốn.

Tuy nhiên, một cách tiếp cận tốt hơn sẽ là sử dụng template hàm vì bạn có thể làm cùng một công việc mà phải viết ít cũng như dễ bảo trì mã nguồn hơn.

Làm thế nào để khai báo một template hàm?

Một template hàm bắt đầu với từ khóa template và theo sau bởi các tham số template trong < > rồi tiếp đến là khai báo hàm.

template <class T>
T someFunction(T arg)
{
   ... .. ...
}

Trong đoạn mã nguồn trên, T là một đối số template chấp nhận các kiểu dữ liệu khác nhau (int, float) và class là một từ khóa.

Bạn cũng có thể sử dụng từ khóa typename thay thế cho class trong ví dụ trên.

Khi, một đối số của một kiểu dữ liệu được truyền vào someFunction(), trình biên dịch sẽ sinh ra một phiên bản mới của hàm someFunction() cho kiểu dữ liệu đó.

Ví dụ 1: Template hàm giúp tìm số lớn nhất

Chương trình hiển thị số lớn nhất trong hai số sử dụng templates hàm

// Nếu hai ký tự được truyền vào template hàm, ký tự với giá trị ASCII lớn hơn sẽ được hiển thị.

#include <iostream>
using namespace std;

// hàm template
template <class T>
T Large(T n1, T n2)
{
	return (n1 > n2) ? n1 : n2;
}

int main()
{
	int i1, i2;
	float f1, f2;
	char c1, c2;

	cout << "Enter two integers:\n";
	cin >> i1 >> i2;
	cout << Large(i1, i2) <<" is larger." << endl;

	cout << "\nEnter two floating-point numbers:\n";
	cin >> f1 >> f2;
	cout << Large(f1, f2) <<" is larger." << endl;

	cout << "\nEnter two characters:\n";
	cin >> c1 >> c2;
	cout << Large(c1, c2) << " has larger ASCII value.";

	return 0;
}

Đầu ra

Enter two integers:

5

10

10 is larger.

 

Enter two floating-point numbers:

12.4

10.2

12.4 is larger.

 

Enter two characters:

z

Z

z has larger ASCII value.

Trong chương trình trên, một template hàm Large() được định nghĩa với hai đối số là n1n2 thuộc kiểu dữ liệu T. T chỉ định rằng đối số có thể có bất kỳ kiểu gì.

Hàm Large() trả về giá trị lớn nhất trong hai đối số sử dụng phép toán điều kiện đơn giản.

Trong hàm main(), các biến thuộc ba kiểu dữ liệu khác nhau: int, floatchar được khai báo. Các biến này được truyền vào template hàm Large() như hàm bình thường.

Trong quá trình chạy, khi một số nguyên được truyền vào hàm template, trình biên dịch sẽ biết nó cần phải sinh ra một hàm Large() mới phù hợp với đối số dạng int.

Tương tự, khi dữ liệu dạng dấu phẩy động và ký tự được truyền vào, nó biết dạng đối số và sinh ra hàm Large() tương ứng.

Theo cách này, chỉ cần sử dụng 1 template hàm duy nhất để thay thế cho ba hàm thông thường giống nhau và qua đó giúp mã nguồn của bạn dễ bảo trì hơn.

Ví dụ 2: Đổi chỗ giá trị sử dụng template hàm

Chương trình giúp đổi chỗ dữ liệu sử dụng template hàm

#include <iostream>
using namespace std;

template <typename T>
void Swap(T &n1, T &n2)
{
	T temp;
	temp = n1;
	n1 = n2;
	n2 = temp;
}

int main()
{
	int i1 = 1, i2 = 2;
	float f1 = 1.1, f2 = 2.2;
	char c1 = 'a', c2 = 'b';

	cout << "Before passing data to function template.\n";
	cout << "i1 = " << i1 << "\ni2 = " << i2;
	cout << "\nf1 = " << f1 << "\nf2 = " << f2;
	cout << "\nc1 = " << c1 << "\nc2 = " << c2;

	Swap(i1, i2);
	Swap(f1, f2);
	Swap(c1, c2);

        cout << "\n\nAfter passing data to function template.\n";
	cout << "i1 = " << i1 << "\ni2 = " << i2;
	cout << "\nf1 = " << f1 << "\nf2 = " << f2;
	cout << "\nc1 = " << c1 << "\nc2 = " << c2;

	return 0;
}

Đầu ra

Before passing data to function template.

i1 = 1

i2 = 2

f1 = 1.1

f2 = 2.2

c1 = a

c2 = b

 

After passing data to function template.

i1 = 2

i2 = 1

f1 = 2.2

f2 = 1.1

c1 = b

c2 = a

Trong chương trình này, thay vì gọi một hàm bằng cách truyền tham trị, một lời gọi bằng cách truyền tham chiếu được thực hiện.

Template hàm Swap() nhận vào hai đối số và đổi chỗ chúng bằng tham chiếu.

Template lớp

Giống như template hàm, bạn có thể tạo template lớp cho các thao tác trên các lớp tương đồng.

Đôi khi, bạn cần một bản lập trình lớp giống nhau cho toàn bộ các lớp, duy chỉ có kiểu dữ liệu là khác biệt.

Thông thường, bạn sẽ cần tạo các lớp khác nhau cho các kiểu dữ liệu khác nhau hoặc tạo các biến thành viên khác nhau cùng với các hàm trong cùng một lớp duy nhất.

Điều này sẽ khiến lượng mã nguồn của bạn trở nên khổng lồ và sẽ rất khó để bảo trì, vì một thay đổi trong một hàm/lớp sẽ phải được lặp lại cho toàn bộ các lớp/hàm.

Tuy nhiên, template lớp khiến việc sử dụng mã nguồn giống nhau trở nên dễ dàng cho toàn bộ các kiểu dữ liệu.

Làm thế nào để khai báo template lớp?

template <class T>
class className
{
   ... .. ...
public:
   T var;
   T someOperation(T arg);
   ... .. ...
};

Trong khai báo trên, T là đối số template được sử dụng để giữ chỗ cho kiểu dữ liệu cần sử dụng.

Trong thân lớp, một biến thành viên var và hàm thành viên someOperation() đều có kiểu T.

Cách tạo một đối tượng template lớp?

Để tạo một đối tượng template lớp, bạn cần khai báo kiểu dữ liệu bên trong một < > khi khởi tạo.

className<dataType> classObject;

Lấy ví dụ:

className<int> classObject;
className<float> classObject;
className<string> classObject;

Ví dụ 3: Máy tính đơn giản sử dụng template lớp

Chương trình giúp cộng, trừ, nhân và chia hai số sử dụng template lớp.

#include <iostream>
using namespace std;

template <class T>
class Calculator
{
private:
	T num1, num2;
	
public:
	Calculator(T n1, T n2)
	{
		num1 = n1;
		num2 = n2;
	}
	
	void displayResult()
	{
		cout << "Numbers are: " << num1 << " and " << num2 << "." << endl;
		cout << "Addition is: " << add() << endl;
		cout << "Subtraction is: " << subtract() << endl;
		cout << "Product is: " << multiply() << endl;
		cout << "Division is: " << divide() << endl;
	}
	
	T add() { return num1 + num2; }
	
	T subtract() { return num1 - num2; }
	
	T multiply() { return num1 * num2; }
	
	T divide() { return num1 / num2; }
};

int main()
{
	Calculator<int> intCalc(2, 1);
	Calculator<float> floatCalc(2.4, 1.2);
	
	cout << "Int results:" << endl;
	intCalc.displayResult();
	
	cout << endl << "Float results:" << endl;
	floatCalc.displayResult();
	
	return 0;
}

Đầu ra

Int results:

Numbers are: 2 and 1.

Addition is: 3

Subtraction is: 1

Product is: 2

Division is: 2

 

Float results:

Numbers are: 2.4 and 1.2.

Addition is: 3.6

Subtraction is: 1.2

Product is: 2.88

Division is: 2

Trong chương trình trên, một template lớp Calculator được khai báo.

Lớp này chứa hai thành viên private có kiểu T:num1 & num2, và một hàm tạo để khởi tạo các đối tượng.

Nó cũng chứa các hàm thành viên public để tính toán cộng, trừ, nhân và chia của các số, sau đó trả về giá trị của kiểu được định nghĩa bởi người dùng. Tương tự, một hàm displayResult() được sử dụng để hiển thị kết quả cuối cùng ra màn hình.

Trong hàm main(), hai đối tượng Calculator khác nhau là intCalcflatCalc được tạo ra cho kiểu dữ liệu: intfloat. Các giá trị được khởi tạo sử dụng hàm tạo.

Lưu ý rằng chúng ta sử dụng <int><float> khi tạo các đối tượng. Điều này sẽ giúp trình biên dịch biết kiểu dữ liệu sử dụng cho việc tạo lớp.

Điều này tạo ra một định nghĩa lớp cho mỗi kiểu dữ liệu intfloat, hai kiểu này sẽ được sử dụng sau đó.

Sau đó, displayResult() của cả hai đối tượng được gọi và thực hiện tính toán cũng như hiển thị kết quả.

** Nếu bạn muốn viết các nội dung đặt biệt thì hãy làm theo hướng dẫn sau

Xem thêm 10 bình luận
Viết blog mới của bạn
Báo lỗi trang
Đang tải