Với khuôn hình, ta có thể ñịnh nghĩa một mẫu cho một họ các hàm tương ứng, bằng cách xem kiểu dữ liệu như một tham số ñối.. Khai báo khuôn hình hàm Các ñối ứng với các kiểu dữ liệu ñược
Trang 1Chương 9
KHUÔN HÌNH (TEMPLATE)
§ 1 KHUÔN HÌNH HÀM
1.1 Khái niệm khuôn hình hàm
Xét hàm max(x,y) ñể tính max của hai ñối Rõ ràng x và y có thể có bất kỳ kiểu nào (kiểu chuẩn của C++, kiểu cấu trúc, kiểu hợp hoặc kiểu ñối tượng) miễn sao chúng có một thứ tự nào ñấy
Nếu không dùng khuôn hình, thì cần xây dựng nhiều hàm (ñược ñịnh nghĩa chồng) ứng với các kiểu khác nhau của x và y
Với khuôn hình, ta có thể ñịnh nghĩa một mẫu cho một họ các hàm tương ứng, bằng cách xem kiểu dữ liệu như một tham số (ñối)
1.2 Khai báo khuôn hình hàm
Các ñối ứng với các kiểu dữ liệu ñược khai báo như sau:
template <class T1, class T2, , class Tm>
Chú ý:
+ Khuôn hình hàm trong ñó có sử dụng các ñối T1, T2, ,Tm phải ñược viết ngay sau dòng khai báo nói trên
+ Các ñối T1, T2, ,Tm là ñối của chỉ khuôn hình hàm này
1.3 Cách xây dựng khuôn hình hàm
Các khuôn hình hàm ñược xây dựng sau khai báo các ñối như trong 1.2 Nội dung một khuôn hình hàm cũng giống như một hàm thông thường, ngoại trừ một ñiều: các kiểu dữ liệu cụ thể ñược thay bằng các ñối khai báo ở trên
Ví dụ khuôn hình hàm max ñược xây dựng như sau:
template <class T>
T max(T x, T y)
{
return (x>y) ? x : y ;
}
1.4 Cách sử dụng khuôn hình hàm
Khuôn hình hàm ñươc sử dụng thông qua lời gọi hàm Khi sử dụng khuôn hình này trong chương trình thì Trình biên dịch sẽ sản sinh một hàm tương ứng với kiểu
dữ liệu trong lời gọi
Trang 2Ví dụ:
Sử dụng khuôn hình hàm max
class A { … };
int a, b, c;
A u, v, w;
c = max(a,b);
w = max(u,v);
Chú ý: Có thể dùng khuôn hình hàm max ñối với các kiểu dữ liệu chuẩn của C++
cũng như kiểu ñối tượng bất kỳ, miễn sao có thể áp dụng phép so sánh >
1.5 ðịnh nghĩa chồng khuôn hình hàm
Có thể dùng cùng một tên ñể ñịnh nghĩa các khuôn hình hàm với một trong các khác biệt sau:
+ Khác nhau về ñối: kiểu ñối, số ñối
+ Khác nhau về số kiểu dữ liệu
Ví dụ:
template <class T>
T operator*(T , T) // Nhân 2 ñối tượng cùng kiểu
template <class T>
T operator*(T* , int) // Nhân dẫy ñối tượng cùng kiểu
template <class T1, class T2>
T operator*(T1 , T2) // Nhân 2 ñối tượng khác kiểu
// ví dụ ma trận và véc tơ
§ 2 CÁC VÍ DỤ VỀ KHUÔN HÌNH HÀM
Ví dụ 1:
/*
Gom khuôn hình hàm:
template <class T>
void sap_xep(T *x, int n)
template <class T>
void nhap_day_dt(T *dt, int &n)
template <class T>
void xuat_day_dt(T *dt, int &n)
ðối T biểu thi ba kiểu:
+ int
+ double
+ TS (lop)
*/
Trang 3#include <iostream.h>
#include <conio.h>
#include <math.h>
class TS
{
private:
char ht[25];
double td;
public:
friend istream& operator>>(istream &in, TS &t)
{
cout << "\nHo ten: ";
in.get(t.ht,25);
cout << "Tong diem: ";
in >> t.td;
in.ignore();
return in;
}
friend ostream& operator<<(ostream &out,const TS &t)
{
out << "\nHo ten: " << t.ht << " Tong diem: " << t.td;
return out;
}
friend int operator>(const TS &ts1,const TS &ts2)
{
return (ts1.td>ts2.td) ;
}
} ;
template <class T>
void sap_xep(T *x, int n)
{
int i,j;
T tg;
for(i=1; i<n; ++i)
for(j=i+1; j<=n; ++j)
if( !(x[i]>x[j]) )
{
tg=x[i]; x[i]=x[j]; x[j]=tg;
}
Trang 4template <class T>
void nhap_day_dt(T *dt, int &n)
{
cout << "\nSo doi tuong: ";
cin >> n; cin.ignore();
for(int i=1; i<=n; ++i)
{
cout << "\nNhap doi tuong thu " << i << " : " ;
cin >> dt[i];
}
}
template <class T>
void xuat_day_dt(T *dt, int n)
{
for(int i=1; i<=n; ++i)
cout << "\nDoi tuong thu " << i << " : " << dt[i];
}
void main()
{
int a[10];
double b[10];
TS t[10];
int n;
nhap_day_dt(a,n);
sap_xep(a,n);
xuat_day_dt(a,n);
nhap_day_dt(b,n);
sap_xep(b,n);
xuat_day_dt(b,n);
nhap_day_dt(t,n);
sap_xep(t,n);
xuat_day_dt(t,n);
getch();
}
Ví dụ 2:
/*
Chu y cach khai bao 2 doi khuôn hình:
template <class T1, class T2>
T2 nhan(T1 a, T2 x) { }
Trang 5*/
#include <iostream.h>
#include <conio.h>
#include <math.h>
#include <iomanip.h>
class VT;
class MT;
class MT
{
private:
int a[20][20];
int n;
public:
friend istream& operator>>(istream &in, MT &x);
friend ostream& operator<<(ostream &out,const MT &x);
VT operator*(const VT &x);
} ;
istream& operator>>(istream &in, MT &x)
{
int i,j;
cout << "\n Cap ma tran N= ";
in >> x.n;
for(i=1; i<=x.n; ++i)
for(j=1; j<=x.n; ++j)
{
cout << "a[" <<i<<"]["<<j<<"]= ";
in >> x.a[i][j];
}
return in;
}
ostream& operator<<(ostream &out,const MT &x)
{
int i,j;
for(i=1; i<=x.n; ++i) {
out << "\n";
for(j=1; j<=x.n; ++j)
out << setw(5) << x.a[i][j];
}
return out;
}
Trang 6class VT
{
private:
int a[20];
int n;
public:
friend istream& operator>>(istream &in, VT &x);
friend ostream& operator<<(ostream &out,const VT &x);
friend class MT;
} ;
VT MT::operator*(const VT &x)
{
VT y;
int i,j;
y.n=n;
for(i=1; i<=n; ++i)
{
y.a[i]=0;
for(j=1; j<=n; ++j)
y.a[i] += a[i][j]*x.a[j];
}
return y;
}
istream& operator>>(istream &in, VT &x)
{
int i;
cout << "\n Cap vec to N= ";
in >> x.n;
for(i=1; i<=x.n; ++i)
{
cout << "a[" <<i<<"]= " ;
in >> x.a[i];
}
return in;
}
ostream& operator<<(ostream &out,const VT &x)
{
int i;
out << "\n";
for(i=1; i<=x.n; ++i)
Trang 7out << setw(5) << x.a[i];
return out;
}
template <class T1, class T2>
T2 nhan(T1 a, T2 x) { return a*x; }
void main()
{
int k; double x,y;
clrscr();
k = nhan(4,5);
x = nhan(3,5.5);
y = nhan(5.0,6.5);
cout << "\nk= " << k << " x= "<< x <<" y= " << y;
MT a; VT u,v;
cout << "\nNhap MT a:" ; cin>>a;
cout << "\nNhap VT u:" ; cin>>u;
v = a*u;
cout << "\nMa tran A: " << a ;
cout << "\n Vec to u: " << u;
cout << "\n Vec to v=Au: " << v;
getch();
}
§ 3 KHUÔN HÌNH LỚP 3.1 Khái niệm khuôn hình lớp
Có thể dùng từ khoá template ñể xây dựng một khuôn hình lớp trong ñó kiểu thuộc tính ñược xem như các ñối Bằng cách ñó ta có thể tạo ra một họ các lớp giống nhau về bản chất xử lý, nhưng khác nhau về kiểu dữ liệu
3.2 Khai báo khuôn hình lớp
Các ñối ứng với các kiểu dữ liệu ñược khai báo như sau:
template <class T1, class T2, , class Tm>
Chú ý:
+ Khuôn hình lớp trong ñó có sử dụng các ñối T1, T2, ,Tm phải ñược viết ngay sau dòng khai báo nói trên
+ Các ñối T1, T2, ,Tm là ñối của chỉ khuôn hình lớp này
Trang 8Các khuôn hình lớp ñược xây dựng sau khai báo các ñối như trong 3.2 Nội dung một khuôn hình lớp cũng giống như một lớp thông thường, ngoại trừ một ñiều: các
kiểu dữ liệu cụ thể ñược thay bằng các ñối khai báo ở trên
Ví dụ khuôn hình lớp ñược xây dựng như sau:
template <class T>
class List
{
T *v ;
int n;
public:
List(int);
T& operator[](int i)
{
return v[i] ;
}
} ;
// Bên ngoài ñịnh nghĩa lớp
template <class T> List<T>::List(int n)
{
v = new T[n];
size = n;
}
3.4 Cách sử dụng khuôn hình lớp
Có thể dùng khuôn hình lớp ñể tạo một các ñối tượng với các kiểu dữ liệu bất kỳ, bằng câu lệnh khai báo, new, hàm tạo Sau ñó sử dụng các phương thức ñể xử lý các ñối tượng tạo ra
Ví dụ có thể tạo ra các ñối tượng List với kiểu dữ liệu bất kỳ như sau:
List <int> x(20) ;
List <A> u(30);
Sau ñó dùng phương thức chỉ số và phép gán:
x[3] = 7 ;
u[10] = m ; // m là một ñối tượng kiểu A
§ 4 VÍ DỤ VỀ KHUÔN HÌNH LỚP
Trang 9/*
Mot doi T dung cho lop List
Doi T bieu thi 3 kieu:
+ int
+ double
+ TS (lop)
*/
#include <iostream.h>
#include <conio.h>
#include <math.h>
class TS
{
private:
char ht[25];
double td;
public:
friend istream& operator>>(istream &in, TS &t)
{
cout << "\nHo ten: ";
in.get(t.ht,25);
cout << "Tong diem: ";
in >> t.td;
in.ignore();
return in;
}
friend ostream& operator<<(ostream &out,const TS &t)
{
out << "\nHo ten: " << t.ht << " Tong diem: " << t.td;
return out;
}
friend int operator>(const TS &ts1,const TS &ts2)
{
return (ts1.td>ts2.td) ;
}
} ;
template <class T>
class List
{
private:
T *t;
Trang 10public:
List()
{
n=0; t=NULL;
}
List(int );
void sap_xep();
void nhap_day_dt();
void xuat_day_dt();
};
template <class T> List<T>::List(int n1)
{
n=n1;
t = new T[n+1];
}
template <class T> void List <T>::sap_xep()
{
int i,j;
T tg;
for(i=1; i<n; ++i)
for(j=i+1; j<=n; ++j)
if( !(t[i]>t[j]) )
{
tg=t[i]; t[i]=t[j]; t[j]=tg;
}
}
template <class T> void List<T>::nhap_day_dt()
{
if (t!=NULL) delete t;
cout << "\nSo doi tuong: ";
cin >> n; cin.ignore();
t = new T[n+1];
for(int i=1; i<=n; ++i)
{
cout << "\nNhap doi tuong thu " << i << " : " ;
cin >> t[i];
}
}
template <class T> void List<T>::xuat_day_dt()
{
for(int i=1; i<=n; ++i)
Trang 11cout << "\nDoi tuong thu " << i << " : " << t[i];
}
void main()
{
List <int> a;
List <double> b;
List <TS> ts;
a.nhap_day_dt();
a.sap_xep();
a.xuat_day_dt();
b.nhap_day_dt();
b.sap_xep();
b.xuat_day_dt();
ts.nhap_day_dt();
ts.sap_xep();
ts.xuat_day_dt();
getch();
}
BÀI TẬP CHƯƠNG 9 Bài 1.Xây dựng khuôn hình hàm ñể sắp xếp một dãy các phần tử cùng kiểu Sau
ñó hãy áp dụng khuôn hình hàm này ñể sắp xếp một dãy số thực, và một dãy các chuỗi ký tự (kiểu dữ liệu char*)
Bài 2 Hãy chỉ ra những yêu cầu nào ñối với các kiểu dữ liệu có thể truyền ñược
vào cho tham số kiểu của khuôn hình hàm sum dưới ñây:
template <class T>
T sum(T a[], int n)
{
T s=0;
for (int i=0; i<n; ++i)
s+=a[i];
return s;
}
Bài 3 Xây dựng lớp SP (lớp Số phức, sẽ sử dụng trong các bài 4, 5, 6)
class SP
{
float x, y;
Trang 12SP() { x=y=0; }
SP(float x1, float y1)
{
x=x1;
y=y1;
}
// các phương thức khác
};
Bài 4 Xây dựng khuôn hình lớp DS (Dãy số), sau ñó áp dụng cho một lớp dãy số
thực và cho một lớp dãy số phức
template <class T, int n>
class DS
{
T a[n];
public:
T& operator[] (int i)
{
return a[i];
}
// các phương thức khác
};
Bài 5 Xây dựng khuôn hình lớp ngăn xếp (Stack), sau ñó áp dụng cho một lớp
ngăn xếp các số thực và một lớp ngăn xếp các số phức
template <class T>
class Stack
{
int top;
T element[n];
public:
Stack() { top=0; }
void push(T e);
void pop(T& e);
// các phương thức khác
};
Trang 13Bài 6 Xây dựng khuôn hình lớp hàng ñợi (Queue), sau ñó áp dụng cho một lớp
hàng ñợi các số thực và một lớp hàng ñợi các số phức
template <class T>
class Queue
{
int head;
T element[n];
public:
Queue() {head=0; }
void enqueue(T e);
void dequeue(T& e);
// các phương thức khác
};