Nguyễn mẫu hàm con trỏ hàm C++ nâng cao

29 454 0
Nguyễn mẫu hàm con trỏ hàm C++ nâng cao

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Đâ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.

MỘT SỐ KIẾN THỨC NÂNG CAO TRONG C/C++ I.TEMPLATE FUNCTION(KHUÔN MẪU HÀM) Đặt vấn đề rằng ta có đoạn hàm để CT sau: #include <iostream> #include <conio.h> using namespace std; void HoanVi(int &x, int &y) { int temp = x; x = y; y = temp; } void main() { int x = 1, y = 2; HoanVi(x, y); cout<<"x = "<<x<<", y = "<<y; getch(); } Đâ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ằng chú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 10 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ể 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 . 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ới mọi kiễu dữ liệu thì chúng ta sẽ đặt dòng template<class T> hoặc template<typename T>, chúng giống nhau hoàn trong hai trường hợp trên tuy nhiên có một số chỗ chúng ta buộc phải dùng class hoặc buộc phải dùng typename. Ở đâ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à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 { T temp = x; // Thay int temp = T temp để có thể đồng nhất với kiểu của x,y x = y; y = temp; } Như vậy ở bất kỳ 1 hàm nào nếu chúng ta truyền 2 số float thì khi gọi hàm trình compile sẽ "hiểu" rằng T là float, nếu ta truyền vào 2 chuỗi string compile sẽ biến T thành string, thậm chí cả 2 HocSinh, NhanVien, Very good. ^_^ 2 cách gọi hàm có dùng template . 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. VD đ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ả %. #include <stdio.h> #include <conio.h> template<class T> void Sum(T x, T y) { printf("x + y = %d", x + y); // ? tại sao lại là %d khi kiểu dữ liệu T là kiểu chưa //biết trước! } void main() { double x = 1, y = 2; Sum(x,y); // x + y = 0 . Một kết quả ko như mong muốn getch(); } VD đoạn dùng template function theo thư viện <iostream> (input output stream của C++) #include <iostream> #include <conio.h> using namespace std; template<class T> void Sum(T x, T y) { cout<<"x + y = "<<x+y; // OK. Hoàn hảo } void main() { double x = 1, y = 2; Sum(x,y); // x + y = 3 getch(); } - Chúng ta cũng có thể khai báo template với 2 kiểu dữ liệu không biết trước trình compile sẽ xác định kiểu dữ liệu theo thứ tự biến truyền vào hàm VD: #include <iostream> #include <conio.h> using namespace std; template<class T, class Y> // 2 kiểu T và Y void Nhap2Bien(T &x, Y &y) { cout<<"Nhap x:"; cin>>x; cout<<"Nhap y:"; cin>>y; } void main() { int x; char c; Nhap2Bien(x, c); // T sẽ có kiểu int, Y có kiểu char cout<<"x = "<<x<<endl; cout<<"c = "<<c<<endl; long a; double b; Nhap2Bien(a, b); // T sẽ có kiểu long, Y có kiểu double getch(); } CHÚ Ý 2: Chúng ta đã biết rằng hàm có cài đặt template function thí mọi kiễu dữ liệu truyền vào sẽ được tham số hó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) { cout<<"Nhap x:"; cin>>x; cout<<"Nhap y:"; cin>>y; } void main() { int x; string str; Nhap2Bien(x, str); // Y có kiểu string. Nhưng nhìn đoạn hàm trên.Chuỗi mà //chúng ta lại dùng cin??? // Đáng lẽ phải fflush(stdin); rồi getline(cin, y); chứ :) cout<<"x = "<<x<<endl; cout<<"str = "<<str<<endl; getch(); } II. 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 class TenLop { // Nội dung của lớp }; Khi định nghĩa các phương thức của lớp được tham số hóa kiễu dữ liệu như trên ta sử dụng cú pháp như sau: template<class T> KieuTraVe TenLop<T>::TenHam(//các tham số nếu có) { // Nội dung của phương thức } Khác với việc sử dụng khuôn mẫu hàm đối với khuôn mẫu lớp chúng ta phải nếu tường minh kiễu dữ liệu cần sử dụng. Ví dụ trong trường hợp muốn sử dụng lớp Mang1Chieu có kiểu int ta phải viết như sau: Mang1Chieu<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 VD 1: template<class T, int size) class MyArray { private: T a[size]; public: // Các phương thức }; void main() { MyArray<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 } Đặc biệt hơn ta có thể khai báo vừa kiễu dữ liệu biết trước và cả giá trị mặc định trong template như VD2 sau: VD 2: template<class T = int, int size = 50> class MyArray { private: T arr[size]; public: // Các phương thức }; Ở 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 Khi 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: template<class T> class CoSo { //Nội dung của lớp cơ sở public : void NhapCoSo() { // Nội dung phương thức Nhap } }; template<class T> class DanXuat:public CoSo<T> { // Nội dung của lớp dẫn xuất void NhapDanXuat() { CoSo<T>::NhapCoSo(); // Gọi phương thức NhapCoSo của class CoSo } }; VD: #include <iostream> [...]... đế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: = ; Cách 2: = & ; 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",... báo con trỏ hàm trỏ đến các hàm không có kiểu trả //về là với 2 tham 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. .. getch(); } CHÚ Ý:Cẩn thận trong trường hợp hàm NhapCoSo là hàm ảo và có sử dụng tính chất đa xạ Khi đó cách thức hoạt động của hàm có thể khác với mong muốn! III .Con trỏ hàm Biế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: (* )() CHÚ Ý:... con trỏ hàm q đang trỏ đến hàm Nhap2SoNguyen p = TinhTong; // OK Con trỏ p trỏ đến hàm TinhTong q(x, y); Nhap2SoNguyen(x,y); printf("x + y = %d", p(x,y)); } VD2 là sự kết hợp khuôn mẫu hàm và con trỏ hàm để tạo nên tình tùy biến mã nguồn cao nhất template void HoanVi(T* x, T* y) { T temp = *x; *x = *y; *y = temp; } void main() { // Khai báo con trỏ hàm để có thể trỏ tới hàm HoanVi float x,y;... 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 -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... Có 2 cách cho con trỏ trỏ tới hàm Pointer = HoanVi; // C1 Pointer = &HoanVi; // C2 //HoanVi(&x, &y); Pointer(&x, &y); } IV MẢNG CON TRỎ 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í... phải vững con trỏ hàm và cả khuôn mẫu hàm Hãy xác định các hàm có mỗi liên quan, điểm tương đồng để có thể gom chúng lại 1 hàm TuyChon nào đó BÀI TẬP RÈN LUYỆN Hãy thử làm bài tập sau bằng việc ứng dụng con trỏ hàm và khuôn mẫu hàm để tạo nên mã nguồn có tính tùy biến 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ả... 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 } =>Bây giờ hàm đã thực sự có tính tùy biến rất cao Điều quan trọng là biết cách nhận ra điểm giống nhau của các hàm để thêm... 3 là 1 con / /trỏ hàm phù hợp để trỏ đến // 2 hàm SoSanhNho va SoSanhLon { for(int i = 0; i < n - 1; i++) { for(int j = i + 1; j < n; j++) { if(p(a[j], a[i]) == true) { int temp = a[j]; a[j] = a[i]; a[i] = temp; } } } } void main() { int 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... 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 } - Qua ví dụ trên ta đã thấy việc ứng dụng con trỏ hàm để tùy biến mã nguồn Bây giờ ta tăng thêm tính tùy biến khi bổ sung kết hợp cả khuôn mẫu hàm như sau để có thể tùy sửa là mảng . để 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ỏ. 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: <Tên con trỏ& gt; = <Tên hàm& gt;; Cách 2: <Tên con trỏ& gt; =. 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

Ngày đăng: 05/06/2015, 11:44

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan