Đây là 1 đoạn CT hoàn toàn đúng.Nhưng giả sử chúng ta muốn bảo trì CT rằngchúng ta muốn hoán vị 2 số thực kiễu float. Với hàm xây dựng sẵn như trên việc chúng ta truyền 2 biến kiểu float vào cho hàm sẽ bị lỗi biên dịch. Cách khác là chúng ta tạo ra riêng 1 hàm để hoán vị 2 kiểu khác chúng ta muốn nhưng cách này ko khả quan. Nếu 1 chương trình muốn hoán vị tới 10 kiểu khác nhau không lẽ ta phải tạo 1 0 hàm HoanVi. =>Code dài nhìn thiếu chuyên nghiệp, khó khăn trong việc bảo trì, nâng cấp.Rất maymắn C++ cho ra đời template function(khuôn mẫu hàm) để ta có thể thamsố hóa kiễu dữ liệu được truyền vào 1 hàm.Một hàm được tham số hóa là coi như là 1 hàm tổng quát xài chung cho mọi kiểudữ liệu từ cơ sơ cho đến kiễu dữ liệu định nghĩa.Chúng ta chỉ cần cài đặt hàm 1 lầnnhưng sử dụng được với nhiều kiểu dữ liệu .CÁCH THỰC HIỆN.Đối với những hàm chúng ta xác định rằng hàm đó sẽ có thể dùng chung đối vớimọi kiễu dữ liệu thì chúng ta sẽ đặt dòng template hoặctemplate, chúng giống nhau hoàn trong hai trường hợp trên tuynhiên có một số chỗ chúng ta buộc phải dùng class hoặc buộc phải dùngtypename. Ở đây ta sẽ chỉ dùng class T (theo tôi thấy được sử dụng rộng rãi hơn) Sau khi đã khai báo template cho hàm thì ta sẽ thay đổi kiễu dữ liệu trong hàmmà ta cho rằng chúng sẽlà 1 kiễu dữ liệu không biết trước bởi vì đối với hàm cóxây dựng template thì trình compile sẽ thực sự những biến đó thuộc kiễu dữ liệunào chỉ khi ta truyền biến vào hàm.
Trang 1MỘT SỐ KIẾN THỨC NÂNG CAO TRONG C/C++
I.TEMPLATE FUNCTION(KHUÔN MẪU HÀM)
Trang 2Rất maymắn C++ cho ra đời template function(khuôn mẫu hàm) để ta có thể tham
số hóa kiễu dữ liệu được truyền vào 1 hàm
Một hàm được tham số hóa là coi như là 1 hàm tổng quát xài chung cho mọi kiểu
dữ liệu từ cơ sơ cho đến kiễu dữ liệu định nghĩa.Chúng ta chỉ cần cài đặt hàm 1 lần nhưng sử dụng được với nhiều kiểu dữ liệu
Trang 3- Sau khi đã khai báo template cho hàm thì ta sẽ thay đổi kiễu dữ liệu trong hàm
mà ta cho rằng chúng sẽlà 1 kiễu dữ liệu không biết trước bởi vì đối với hàm có
xây dựng template thì trình compile sẽ thực sự những biến đó thuộc kiễu dữ liệu
nào chỉ khi ta truyền biến vào hàm
VD ta sửa lại đoạn HoanVi trên cho nó có tính tham số hóa
template<class T> // T là tên kiễu dữ liệu coi như là chưa biết trước Các bạn có //thể đặt tên khác tùy ý
HoanVi(T &x, T &y) // Tôi thay kiểu int thành 1 kiểu T
C1: Gọi tường minh TenHam<kiểu dữ liệu truyền vào>(//Tham số nếu có)
C2: Gọi không tường mình thì như chúng ta gọi bình thường ^^ là như thế này
TenHam(//Tham số nếu có)
Theo kinh nghiệm của tôi thì trong 1 số trường hợp trình compile yêu cầu bạn phải
gọi tường minh đó ^^
CHÚ Ý 1: Chỉ khi ta xài các phương thức nhập xuất(cout và cin) của C++ thì mới
nên dùng template function
vì C++ việc nhập xuất không cần quan tâm đến đặc tả % của dữ liệu
Trang 4VD đoạn dùng template function theo nhập xuất của thư viện <stdio.h> (standard input output của C )
như sau vẫn bị lỗi vì "vướng" phải đặc tả %
Trang 5template<class T, class Y> // 2 kiểu T và Y
void Nhap2Bien(T &x, Y &y)
Trang 6hóa nhưng ta cũng nên quản lý việc truyền kiểu dữ liệu 1 cách cẩn thận
Ví dụ về việc sử dụng tham số hóa không hợp lý
#include <iostream>
#include <conio.h>
#include <string> // Thư viện string của C++
using namespace std;
template<class T, class Y> // 2 kiểu T và Y
void Nhap2Bien(T &x, Y &y)
Trang 7II CLASS TEMPLATE( KHUÔN MẪU LỚP)
1.Tham số hóa cho lớp
- Tương tự như khuôn mẫu hàm, khuôn mẫu lớp có cú pháp sử dụng như sau
VD1:
template<class T> // T hay tên gì khác tùy bạn đặt
Trang 8Mang1Chieu<int> tendoituong;
2.Các tham số không kiểu
Chúng ta cũng có thể khai bao kiểu dữ liệu mặc định trong template
Trang 9MyArray<int,50> a; // Mảng a có kiểu dữ liệu int với tối đa 50 phần tử
MyArray<HOCSINH, 100> h; // Mảng h có kiểu dữ liệu HOCSINH ( Gia sử ta //đã xây dựng class HOCSINH)// với tối đa 100 em
Ở hàm main ta chỉ việc khai báo MyArray<> tendoituong; thì trình compile cũng
tự xác định rằng Mảng tendoituong có kiểu dữ liệu int với tối đa 50 phần tử
3.Kế thừa các lớp tham số hóa
Trang 10Khi 1 class dẫn xuất kế thừa 1 class cơ sở có sử dụng tham số hóa thì cú pháp như sau:
Trang 12Biến đều có địa chỉ trong bộ nhớ
Vậy hàm tồn tại tức nó sẽ có địa chỉ trong bộ nhớ
=> Sẽ có 1 loại con trỏ trỏ đến các hàm Ta gọi là con trỏ hàm
Cú pháp:
<Kiểu trả về của hàm> (*<Tên con trỏ hàm>)(<Danh sách tham số nếu có>)
CHÚ Ý: Phần danh sách tham số ta chỉ cần quan tâm đến kiễu dữ liệu của tham số,
ko cần thiết khai tên tham số(giống khai báo hàm)
TRỎ ĐẾN HÀM: Chỉ có thể trỏ đến các hàm nếu con trỏ hàm phù hợp kiểu trả về, các ds tham số
Cách 1:
Trang 13<Tên con trỏ> = <Tên hàm>;
Cách 2:
<Tên con trỏ> = &<Tên hàm>;
Sau khi trỏ thay vì gọi hàm bằng cách thông thường ta có thể thay tên hàm bằng tên con trỏ
VD1:
void Nhap2SoNguyen(int &x, int &y)
{
printf("Nhap x,y:\n");
scanf("%d%d", &x, &y);}
int TinhTong(int x, int y)
Trang 14tham số kiểu int truyền theo kiểu tham chiếu ( tham biến).int (*p)(int , int ); // Khai //báo con trỏ hàm trỏ đến các hàm có kiểu trả là int với 2 tham số kiểu
// int truyền theo kiểu tham trị
q = TinhTong; // ERROR Ko phù hợp để trỏ
q = Nhap2SoNguyen; // OK con trỏ hàm q đang trỏ đến hàm Nhap2SoNguyen
q = &Nhap2SoNguyen; // OK con trỏ hàm q đang trỏ đến hàm Nhap2SoNguyen
p = TinhTong; // OK Con trỏ p trỏ đến hàm TinhTong
Trang 15// Khai báo con trỏ hàm để có thể trỏ tới hàm HoanVi
float x,y;
x = 1;
y = 2;
void (*Pointer)(float*, float*);
// Có 2 cách cho con trỏ trỏ tới hàm
- Là mảng lưu giữ các phần tử là biến con trỏ hàm
CÚ PHÁP: KieuTraVe(*TenConTroHam[SoLuongphantu])(//Ds tham số nếu có)
Chú ý rằng các biến con trỏ của mảng phải cùng kiểu tra ve, DS tham số
VD:
int (*array[2])(int , int);
array[0] = TinhTong; // Con trỏ tại vị trí 0 trong mảng trỏ đến hàm TinhTong
array[1] = TinhHieu; // Con trỏ tại vị trí 1 trong mảng trỏ đến hàm TinhHieu
V KHUÔN MẪU HÀM, CON TRỎ HÀM TRONG VIỆC XÂY
DỰNG CHƯƠNG TRÌNH CÓ TÍNH CHẤT TÙY BIẾN MÃ NGUỒN
Trang 16-Ta đã tìm hiểu về khuôn mẫu hàm, con trỏ hàm và các công dụng của chúng Và công dụng lớn nhất
của chúng theo đánh giá của tôi là sự kết hợp để tạo nên mã nguồn có tính tùy biến, ngắn gọn, dễ bảo trì
Ví dụ rằng ta có 2 hàm sắp xếp tăng dần và giảm dần mảng 1 chiều như sau:
void SapTang(int a[], int n)
Trang 17tăng, vừa làm công việc sắp giảm được ko?
Câu trả lời rằng hãy ứng dụng con trỏ hàm như các ví dụ sau:
VD1:
//Ta tạo ra 2 hàm sosanh
//Nhắc lại về kiễu dữ liệu bool của C++:bool là kiểu dữ liệu do C++ xây dựng nên thuận tiện trong việc kiểm tra 1 điều kiện gì đó Nó trả ra 2 giá trị true hoặc false
bool SoSanhNho(int x, int y)
{
if(x > y)
Trang 19int a[5] = {1, 5, 3, 2, 0}; // Khởi tạo mảng a gồm 5 phần tử
SapTuyChon(a, 5, NhoHon); // Ta truyền tên hàm NhoHon vào tham số thứ 3 //Lúc này con trỏhàm p trong hàm SapTuyChon sẽ trỏ vào hàm NhoHon Như vậy câu if trong hàm tương đuong
// if(SoSanhNho(a[j], a[i]) == true) => Hàm đang thực hiện sắp xếp tăng
SapTuyChon(a, 5, LonHon); // Hàm đang thực hiện sắp giảm
Trang 21// if(SoSanhNho(a[j], a[i]) == true) => Hàm đang thực hiện sắp xếp tăng
SapTuyChon(a, 5, LonHon); // Hàm đang thực hiện sắp giảm
Trang 23printf("Max = %f", MaxorMin(a,10, LonHon)); // Tìm max và trả về là 69
printf("Max = %f", MaxorMin(a, 10, NhoHon)); //Tìm min và trả về là -13.2
getch();
}
VD3:Thực hiện tính tổng các số nguyên tố, số chính phương, số hoàn thiện
Thử tưởng tượng rằng nếu ta chưa biết đến các tùy biến mã nguồn thì mã nguồn chúng ta sẽ như sau:
Ở đây tôi không dùng template function được lý do là vì số nguyên tố , số hoàn hảo ngoài truyển kiểu
int ra thì truyền kiểu khác sao được ^_< Điều hiển nhiên nhé
bool KiemTraNguyenTo(int x) // Kiểm tra 1 số x có phải là số nguyên tố không
Có nhiều thuật toán để làm bài này
{
if(x < 2)
return false;
else if(x > 2)
Trang 25bool KiemTraHoanThien(int x) // Hàm kiểm tra 1 số có phải là số hoàn thiện không
// Gio ta phải tạo ra 3 hàm tính tổng
int TongNguyenTo(int a[],int n)
Trang 27=> Đến đây bạn đã nhận ra rằng tại sao ta lại phải tốn công tạo ra 3 hàm như vậy khi công dụng của chúng đều là tính tổng và chỉ khác biệt đoạn code if
Với ví dụ trên tôi sẽ sử dụng con trỏ hàm để tăng tính tùy biến mã nguồn
Tôi thay 3 hàm tính tổng với chỉ 1 hàm duy nhất là TongTuyChon
int TongTuyChon(int a[], int n, bool (*p)(int))
// Ở hàm main tôi sẽ tùy trường hợp mà truyền vào tham số thứ 3 tên hàm kiểm tra
//để con trỏ hàm p trỏ đến nằm cùng địa chỉ với hàm đó
void main()
{
int a[5] = {1, 3, 5, 12, 6};
printf("Tong nguyen to = %d", TongTuyChon(a, n, KiemTraNguyenTo)); //8
printf("Tong hoan thien = %d", TongTuyChon(a, n, KiemTraHoanThien)); // 6
Trang 28printf("Tong Chinh phuong = %d", TongTuyChon(a, n, KiemTraChinhPhuong)); /// 5
1 Xây dựng mảng 1 chiều với kiểu dữ liệu " bất kỳ" thực hiện các yêu cầu sau:
Chú ý hàm nào trả về giá trị được thì phải tạo ra hàm trả về giá trị Ko được tạo hàm không kiểu( hàm void)
+ Đếm số dương
+ Đếm số âm
+ Tính tổng số dương
+ Tính tổng số âm
+ Tính tổng các số cực tiểu Số cực tiểu là số lớn nhỏ 2 số xung quang nó (a[i]
<a[i-1] && a[i] < a[i+1])
+ Tính tổng các số cực đại Số cực đại là số lớn hơn 2 số xung quang nó (a[i] 1] && a[i] > a[i+1])
>a[i-+ Xóa các số lớn hơn 100
+ Xóa các số nhỏ hơn 100
Trang 2915-02-2015 Nguyễn Ái Tuấn