Kỹ thuật lập trình - Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp

9.1 Khuônmẫuhàm

-Vaitròcủakhuônmẫuhàm

-Địnhnghĩakhuônmẫuhàm

-Sửdụngkhuônmẫuhàm

9.2 Khuônmẫulớp

-Địnhnghĩakhuônmẫulớp

-Dẫnxuấtkhuônmẫulớp

-VídụkhuônmẫulớpVector

pdf23 trang | Chuyên mục: C/C++ | Chia sẻ: dkS00TYs | Lượt xem: 3149 | Lượt tải: 2download
Tóm tắt nội dung Kỹ thuật lập trình - Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
/ max(double, double)
swap(N1,N2); // swap(int&,int&)
swap(D1,D2); // swap(double&,double&)
D = max(D1,A1); // error: ambiguous
N = max('c',A1); // error: ambiguous
D = max(D1,A1);// OK: explicit qualification
N = max('c',A); // OK: explicit qualification
}
Khuôn mẫu hàm
Hàm khuôn mẫu
6Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Khả năng áp dụng khuônmẫu hàm
ƒ Khả năng áp dụng một khuôn mẫu hàm là vô tận, nhưng không
phải áp dụng ₫ược cho tất cả các ₫ối số khuôn mẫu
Ví dụ: Điều kiện ràng buộc ₫ối với kiểu dữ liệu có thể áp dụng
trong khuôn mẫu hàm max là phải có phép so sánh lớn hơn (>):
template 
inline T max(T a, T b) { return (a > b)? a : b;} 
=> Đối với các kiểu dữ liệu mới, muốn áp dụng ₫ược thì cần phải
nạp chồng toán tử so sánh >
ƒ Tuy nhiên, khả năng áp dụng ₫ược chưa chắc ₫ã có ý nghĩa
ƒ Ví dụ: Xác ₫ịnh chuỗi ký tự ₫ứng sau trong hai chuỗi cho trước
theo vần ABC
char city1[] = "Ha Noi", city2[] = "Hai Phong";
char* city = max(city1,city2); // ???
// max(char*,char*)
7Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Nạp chồng khuônmẫu hàm
ƒ Một khuôn mẫu hàm có thể ₫ược nạp chồng bằng hàm cùng tên...
char* max(char* a, char* b) { if (strcmp(a,b))... }
void f() {
char c = max('H','K'); // max(char,char)
char city1[] = "Ha Noi", city2[] = "Hai Phong";
char* city = max(city1,city2); // max(char*,char*)
...
}
ƒ ...hoặc bằng một khuôn mẫu hàm cùng tên (khác số lượng các
tham số hoặc kiểu của ít nhất một tham số), ví dụ:
template T max(T a, T b, T c) {...}
template T max(T* a, int n) {...}
nhưng không ₫ược như thế này:
template X max(X a, X b) {...}
8Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Tham số khuônmẫu
ƒ Tham số khuôn mẫu hàm có thể là một kiểu cơ bản hoặc một
kiểu dẫn xuất, nhưng không thể là một biến hoặc một hằng số:
template max(T a, T b) { ... } // OK
template max(int* a) { ... } // error
ƒ Một khuôn mẫu hàm có thể có hơn một tham số kiểu:
template void swap(A& a, B& b) {
A t = a;
a = b; // valid as long as B is compatible to A
b = t; // valid as long as A is compatible to B
}
void f() {
double a = 2.0;
int b = 3;
swap(a,b); // swap(double&,int&)
swap(b,a); // swap<int,double)(int&, double&)
}
9Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
ƒ Thông thường, tham số khuôn mẫu xuất hiện ít nhất một lần là
kiểu hoặc kiểu dẫn xuất trực tiếp của các tham biến:
template void f1(X a, int b) {...}
template void f2(X* b) {...}
template void f3(Y& a, X b) {...}
ƒ Theo chuẩn ANSI/ISO C++, tham số khuôn mẫu không bắt buộc
phải xuất hiện trong danh sách tham biến, nhưng cần lưu ý khi
sử dụng. Ví dụ
#include 
template X* array_alloc(int nelem) { 
return (X*) malloc(nelem*sizeof(X));
} 
void main() {
double* p1 = array_alloc(5); // error!
double* p2 = array_alloc(5); // OK!
...
free(p2);
}
10Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Khuônmẫu hàm và hàm khuônmẫu
ƒ Khuôn mẫu hàm chỉ ₫ưa ra cách thức thực hiện và sử dụng một
thuật toán nào ₫ó một cách tổng quát
ƒ Trong khi biên dịch khuôn mẫu hàm, compiler chỉ kiểm tra về
cú pháp, không dịch sang mã ₫ích
ƒ Mã hàm khuôn mẫu ₫ược compiler tạo ra (dựa trên khuôn mẫu
hàm) khi và chỉ khi khuôn mẫu hàm ₫ược sử dụng với kiểu cụ
thể
ƒ Nếu một khuôn mẫu hàm ₫ược sử dụng nhiều lần với các kiểu
khác nhau, nhiều hàm khuôn mẫu sẽ ₫ược tạo ra tương ứng
ƒ Nếu một khuôn mẫu hàm ₫ược sử dụng nhiều lần với các kiểu
tương ứng giống nhau, compiler chỉ tạo ra một hàm khuôn mẫu.
11Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Ưu ₫iểmcủa khuônmẫu hàm
ƒ Tiết kiệm ₫ược mã nguồn => dễ bao quát, dễ kiểm soát lỗi, nâng
cao hiệu quả lập trình
ƒ Đảm bảo ₫ược tính chặt chẽ về kiểm tra kiểu mạnh trong ngôn
ngữ lập trình (hơn hẳn sử dụng macro trong C)
ƒ Tính mở, nâng cao giá trị sử dụng lại của phần mềm: thuật toán
viết một lần, sử dụng vô số lần
ƒ Đảm bảo hiệu suất tương ₫ương như viết tách thành từng hàm
riêng biệt
ƒ Cho phép xây dựng các thư viện chuẩn rất mạnh (các thuật toán
thông dụng như sao chép, tìm kiếm, sắp xếp, lựa chọn, ....)
12Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Nhược ₫iểmcủa khuônmẫu hàm
ƒ Nếu muốn ₫ảm bảo tính mở hoàn toàn thì người sử dụng khuôn
mẫu hàm cũng phải có mã nguồn thực thi
— Mã nguồn thực thi cần ₫ược ₫ặt trong header file
— Khó bảo vệ chất xám
ƒ Việc theo dõi, tìm lỗi biên dịch nhiều khi gặp khó khăn
— Lỗi nhiều khi nằm ở mã sử dụng, nhưng lại ₫ược báo trong mã ₫ịnh
nghĩa khuôn mẫu hàm
— Ví dụ: Compiler không báo lỗi ở dòng lệnh sau ₫ây, mà báo lỗi ở
phần ₫ịnh nghĩa hàm max, tại phép toán so sánh lớn hơn không
₫ược ₫ịnh nghĩa cho kiểu Complex:
Complex a, b;
... 
Complex c = max(a,b);
ƒ Định nghĩa và sử dụng không ₫úng cách có thể dẫn tới gia tăng
lớn về mã ₫ích, bởi số lượng hàm khuôn mẫu có thể ₫ược tạo ra
quá nhiều không cần thiết.
13Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Ví dụ: khuônmẫu hàm copy
template 
void copy(const S * s, D* d, int n) {
while (n--) 
*d++ = *s++;
}
void main() {
int a[] = {1,2,3,4,5,6,7};
double b[10];
float c[5];
copy(a,b,7); // copy(a,b,7)
copy(b,c,5); // copy(b,c,5);
...
}
14Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
9.2 Khuônmẫu lớp (class template)
ƒ Nhiều cấu trúc dữ liệu như Point, Complex, Vector, List, Map,... 
trước kia vẫn phải ₫ược ₫ịnh nghĩa riêng cho từng kiểu dữ liệu 
phần tử cụ thể, ví dụ DoubleComplex, FloatComplex, 
DoubleVector, IntVector, ComplexVector, DateList, 
MessageList, ...
ƒ Cách thực hiện mỗi cấu trúc thực ra giống nhau, nói chung 
không phụ thuộc vào kiểu phần tử cụ thể
class IntPoint { int x,y;
public: IntPoint(int x0, int y0) : x(x0), y(y0) {}
...
};
class DoublePoint { double x,y;
public: DoublePoint(double x0, double y0) : x(x0), y(y0) {}
...
};
15Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Định nghĩa khuônmẫu lớp
// Point.h
template 
class Point {
T x, y;
public:
Point(): x(0), y(0) {} 
Point(T x0, T y0) : x(x0), y(y0) {}
Point(const Point&);
void move(T dx, T dy) { x += dx; y+= dy; }
bool inRect(Point p1, Point p2);
//...
};
template 
Point::Point(const Point& b) : x(b.x), y(b.y) {}
template 
bool Point::inRect(Point p1, Point p2) {
return (x >= p1.x && x = p2.x && x <= p1.x) &&
(y >= p1.y && y = p2.y && x <= p1.y);
}
Mỗi hàm thành
viên của một
khuôn mẫu lớp là
một khuôn mẫu
hàm
Tham số khuôn mẫu:
Kiểu hoặc hằng số
16Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Sử dụng khuônmẫu lớp: Lớp khuônmẫu
#include "Point.h"
void main() {
Point A1(5,5),A2(10,10);
Point A3(A1);
while (A3.inRect(A1,A2))
A3.move(2,3);
typedef Point FPoint;
FPoint B1(5.0,5.0), B2(10.0,10.0);
FPoint B3(B1);
while (B3.inRect(B1,B2))
B3.move(2,3);
//...
Point C1(B1); // error
if (A3.inRect(B1,B2)) // error
; //...
}
17Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Những kiểu nào có thể áp dụng?
ƒ Khả năng áp dụng của kiểu là vô tận, tuy nhiên không có nghĩa
là áp dụng ₫ược cho tất cả các kiểu
ƒ Một kiểu muốn áp dụng ₫ược phải hỗ trợ các phép toán ₫ược sử
dụng trong mã thực thi khuôn mẫu lớp.
ƒ Ví dụ khuôn mẫu lớp Point yêu cầu kiểu tọa ₫ộ phải áp dụng
₫ược các phép toán sau ₫ây:
— Chuyển ₫ổi từ số nguyên (trong hàm tạo mặc ₫ịnh)
— Sao chép (trong hàm tạo thứ hai và hàm tạo bản sao)
— Toán tử += (trong hàm move)
— Các phép so sánh >=, <= (trong hàm inRect)
ƒ Việc kiểm tra kiểu ₫ược tiến hành khi sử dụng hàm thành viên
của lớp khuôn mẫu, nếu có lỗi thì sẽ ₫ược báo tạimã nguồn
thực thi khuôn mẫu lớp
18Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Tham số khuônmẫu: kiểu hoặc hằng số
template 
class Array {
T data[N];
public:
Array(const T& x = T(0));
int size() const { return N; }
T operator[](int i) const { return data[i]; }
T& operator[](int i) { return data[i]; }
//...
};
template Array::Array(const T& x) {
for (int i=0; i < N; ++ i) data[i] = x;
}
void main() {
Array a;
Array b; // same as above
Array c; // same as above
//...
}
Tham số mặc ₫ịnh
19Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Dẫn xuất từ khuônmẫu lớp
template class IOBuffer : public Array {
public:
IOBuffer(T x) : Array(x) {}
//...
};
class DigitalIO : public IOBuffer {
public:
DigitalIO(bool x) : IOBuffer(x) {}
//...
};
class AnalogIO : public IOBuffer {
typedef IOBuffer BaseClass;
public:
AnalogIO(unsigned short x) : BaseClass(x) {}
//...
};
void main() {
IOBuffer delayBuf(0);
DigitalIO di(false);
AnalogIO ao(0); //...
}
20Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Ví dụ khuônmẫu lớp Vector
template 
class Vector {
int nelem;
T* data;
public:
Vector() : nelem(0), data(0) {}
Vector(int n, T d);
Vector(int n, T *array);
Vector(const Vector&);
~Vector(); 
int size() const { return nelem; }
T operator[](int i) const { return data[i]; }
T& operator[](int i) { return data[i]; }
private:
void create(int n) { data = new T[nelem=n]; }
void destroy() { if (data != 0) delete [] data; }
};
21Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
template Vector::Vector(int n, T d) {
create(n);
while (n-- > 0)
data[n] = d; 
}
template Vector::Vector(int n, T* p) {
create(n);
while (n-- > 0)
data[n] = p[n]; 
}
template Vector::~Vector() { destroy(); }
template 
Vector::Vector(const Vector& a) {
create(a.nelem);
for (int i=0; i < nelem; ++i)
data[i] = a.data[i];
}
22Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
#include "Point.h"
#include "Vector.h"
void main()
Vector v(5,1.0);
double d = v[0];
v[1] = d + 2.0;
Vector v2(v);
//...
int b[] = {1,2,3,4,5};
Vector a(5,b);
int n = a[0];
a[1] = n + 2;
Vector a2(a);
//...
typedef Vector > Points;
Points lines(5,Point(0.0,0.0));
lines[0] = Point(5.0,5.0);
//...
}
23Chương 9: Khuôn mẫu hàm và khuôn mẫu lớp
Bài tập về nhà
ƒ Xây dựng một khuôn mẫu hàm xác ₫ịnh vị trí (₫ịa chỉ) của phần
tử có giá trị lớn nhất xuất hiện ₫ầu tiên trong một dãy số. Viết
chương trình minh họa sử dụng cho hai kiểu số liệu cụ thể.
ƒ Từ bài tập xây dựng lớp MessageList, tổng quát hóa thành một
khuôn mẫu lớp có tên là List với các phép toán (hàm thành viên) 
cần thiết

File đính kèm:

  • pdfC9_Templates.pdf
Tài liệu liên quan