1. Template là gì ?
Template giống như một "cỗ máy" có khả năng sản xuất ra các hàm và lớp dựa vào kiểu của các tham số. Bằng cách sử dụng template, bạn có thể chỉ cần thiết kế 1 lớp (hàm) mà có khả năng thao tác với nhiều loại dữ liệu khác nhau, thay vì bạn phải tạo nhiều lớp (hàm) khác nhau để thao tác với từng loại dữ liệu khác nhau.
Ví dụ như, để tạo một hàm có tác dụng lấy giá trị nhỏ nhất của 2 số mà không sử dụng template, bạn phải viết các hàm như sau :
// min for ints
int min( int a, int b )
return ( a < b ) ? a : b;
// min for longs
long min( long a, long b )
return ( a < b ) ? a : b;
// min for chars
char min( char a, char b )
return ( a < b ) ? a : b;
//etc...
Nhưng nếu sử dụng template, bạn có thể giảm số lượng hàm phải viết xuống chỉ còn một hàm như sau :
template <class T> T min( T a, T b )
return ( a < b ) ? a : b;
Nhưng tất cả những cái đó ta đều có thể viết mà không cần dùng template ! Tất nhiên bạn hoàn toàn có thể viết mã mà không cần dùng template nhưng nếu dùng template bạn có thể tiết kiệm được công sức viết mã, tăng thêm tính linh động của chương trình và một điều quan trọng là khả năng sử dụng lại cao. Bạn cũng nên chú ý là template là type-safe, có nghĩa là trình biên dịch không tự chuyển kiểu (type cast) mà bạn phải chuyển nó 1 cách tường minh.
Đây là prototype chung cho template :
template < [typelist] [, [ arglist ]] > declaration
decaration phải là một hàm hoặc lớp.
Template giống như một "cỗ máy" có khả năng sản xuất ra các hàm và lớp dựa vào kiểu của các tham số. Bằng cách sử dụng template, bạn có thể chỉ cần thiết kế 1 lớp (hàm) mà có khả năng thao tác với nhiều loại dữ liệu khác nhau, thay vì bạn phải tạo nhiều lớp (hàm) khác nhau để thao tác với từng loại dữ liệu khác nhau.
Ví dụ như, để tạo một hàm có tác dụng lấy giá trị nhỏ nhất của 2 số mà không sử dụng template, bạn phải viết các hàm như sau :
// min for ints
int min( int a, int b )
return ( a < b ) ? a : b;
// min for longs
long min( long a, long b )
return ( a < b ) ? a : b;
// min for chars
char min( char a, char b )
return ( a < b ) ? a : b;
//etc...
Nhưng nếu sử dụng template, bạn có thể giảm số lượng hàm phải viết xuống chỉ còn một hàm như sau :
template <class T> T min( T a, T b )
return ( a < b ) ? a : b;
Nhưng tất cả những cái đó ta đều có thể viết mà không cần dùng template ! Tất nhiên bạn hoàn toàn có thể viết mã mà không cần dùng template nhưng nếu dùng template bạn có thể tiết kiệm được công sức viết mã, tăng thêm tính linh động của chương trình và một điều quan trọng là khả năng sử dụng lại cao. Bạn cũng nên chú ý là template là type-safe, có nghĩa là trình biên dịch không tự chuyển kiểu (type cast) mà bạn phải chuyển nó 1 cách tường minh.
Đây là prototype chung cho template :
template < [typelist] [, [ arglist ]] > declaration
decaration phải là một hàm hoặc lớp.
2. Function template
Function template cho phép bạn tạo ta các hàm có khả năng thao tác với bất cứ đối tượng nào mà không cần overload. Với function template có được 1 tập hợp các hàm dựa trên một đoạn mã giống nhau nhưng lại tác động lên các đối tượng khác nhau. Bạn có thể dùng prototype của function template như một trong 2 cách sau :
template <class identifier> function_declaration;
template <typename identifier> function_declaration;
Hai prototype trên có thể coi là như nhau và được dùng như nhau. Ví dụ :
template <class T> void MySwap( T& a, T& b ){
T c( a );
a = b; b = c;
}
Đoạn mã này định nghĩa một tập hợp các hàm có khả năng hoán vị 2 tham số đưa vào. Từ template này bạn có thể tạo ra các hàm hoán chuyển không chỉ có kiểu int, long mà còn có thể hoán chuyển bất cứ kiểu nào, kể cả các kiểu do bạn tự định nghĩa.
Thêm vào đó funtion template sẽ ngăn cản bạn hoán chuyển các đối tượng (object)khác kiểu, bởi vì complier kiểm tra kiểu của các tham số ngay vào lúc biên dịch (complie time).
Bạn có thể gọi hàm funtion template như một hàm thông thường, không cần thêm bất kỳ cú pháp gì đặc biệt, ví dụ :
int i, j;
char k;
MySwap( i, j ); //OK
MySwap( i, k ); //Error, different types.
Kiểu của các tham số của function template cũng có thể được chỉ ra một cách rõ ràng như sau :
template<class T> void f(T) {...}
void g(char j) {
f<int>(j); //generate the specialization f(int)
}
Khi kiểu của các tham số được chỉ một cách rõ ràng, sự chuyển đổi kiểu sẽ được chuyển một cách tự động sao cho phù hợp, như ví dụ trên thì complier sẽ chuyển (char j) sang kiểu int.
Khi function template được gọi lần đầu tiên cho một kiểu cụ thể, complier tạo ra một instantiation (thể hiện), đây là một phiên bản (version) của template funtion cụ thể cho từng kiểu. "Thể hiện" này sẽ được gọi mỗi khi function được gọi tương ứng với kiểu đó. Nếu bạn có vài "thể hiện" giống nhau thì chỉ một "thể hiện" duy nhất được tạo ra.
3. Class template
Tương tự như function template, bạn có thể sử dụng class template để tạo ra tập hợp các lớp cùng tác động lên nhiều kiểu dữ liệu khác nhau, ví dụ :
template <class T, int i> class TempClass {
public:
TempClass( void );
~TempClass( void );
int MemberSet( T a, int b );
private:
T Tarray[i];
int arraysize;
};
Trong ví dụ này, template class sử dụng 2 tham số, một có kiểu là T và một là int. Tham số T có thể được truyền vào với bất cứ kiểu nào, kể các các struct và các đối tượng. Ví dụ như khi bạn muốn tạo ra một đối tượng dùng để thao tác với kiểu int thì bạn dùng như sau :
TempClass <int> myobject (5, 5);
Còn khi bạn muốn thao tác với kiểu float thì bạn lại khai báo như sau :
TempClass <float> myobject (5, 5);
(Tất nhiên là các tham số của function template và của class template là linh động phụ thuộc vào bạn).
Các thành phần trong class template được định nghĩa có khác chút ít so với những lớp nontemplate :
template <class T, int i>
int TempClass< T, i >::MemberSet( T a, int b ) {
if( ( b >= 0 ) && (b < i) ) {
Tarray[b++] = a;
return sizeof( a );
}
else
return -1;
}
Templates for Constructors and Destructors
template <class T, int i>
TempClass< T, i >::TempClass( void ){
TRACE( "TempClass created.\n" );
}
template <class T, int i>
TempClass< T, i >::~TempClass( void ){
TRACE( "TempClass destroyed.\n" );
}
Template không phải là các hàm, lớp thông thường, chúng được complie dựa theo yêu cầu, có nghĩa là code của template function không được biện dịch (complie) cho đến khi có một thể hiện (instantiation) của nó được đòi hỏi (sử dụng). Ngay lúc đó complier tạo ra một hàm cụ thể cho kiểu dữ liệu cụ thể.
Một số template có sẵn trong C++ :
CArray : template class dùng để tạo ra mảng với các phần tử có kiểu tuỳ ý. CArray cung cấp các mảng tương tự như mảng trong C nhưng với CArray bạn có thể tăng hoặc giảm số lượng các phần tử khi cần thiết. Chỉ số của mảng (array index) luôn bắt đầu từ 0. Các truy suất CArray cũng tương tự như array trong C.
CList : template class dùng để tạo ra list với các phần tử có kiểu tuỳ ý. CList cung cấp các danh sách liên kết đôi đã được sắp xếp.
CMap : template class dùng để tạo ra "map" với các phần tử có key và kiểu tuỳ ý. Giá trị key là duy nhất trong một "map". Dùng CMap bạn có thể coi nó như một cuốn tự điển dể tra cứu 1 cách dễ dàng các phần tử.
CTypedPtrArray : template class for type-safe arrays of pointers.
CTypedPtrList : template class for type-safe lists of pointers.
CTypedPtrMap : template class for type-safe maps with pointers.
Ba template class cuối cũng tương tự như 3 template class trước, chỉ khác chữ "pointer" ;)
Tags:
C plus