Bài giảng Ngôn ngữ lập trình - Bài 7: Khuôn mẫu - Lý Anh Tuấn
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Ngôn ngữ lập trình - Bài 7: Khuôn mẫu - Lý Anh Tuấn", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Tài liệu đính kèm:
- bai_giang_ngon_ngu_lap_trinh_bai_7_khuon_mau_ly_anh_tuan.pdf
Nội dung text: Bài giảng Ngôn ngữ lập trình - Bài 7: Khuôn mẫu - Lý Anh Tuấn
- NGÔN NGỮ LẬP TRÌNH Bài 7: Khuôn mẫu Giảng viên: Lý Anh Tu ấn Email: tuanla@tlu.edu.vn
- Nội dung 1. Khuôn mẫu hàm ◦ Cú pháp, định nghĩa ◦ Sự biên dịch 2. Khuôn mẫu lớp ◦ Cú pháp ◦ Ví dụ: lớp khuôn mẫu mảng 3. Khuôn mẫu và kế thừa ◦ Ví dụ: lớp khuôn mẫu mảng nhập giá trị một phần 2
- Giới thiệu Khuôn mẫu C++ ◦ Cho phép các định nghĩa tổng quát cho hàm và lớp ◦ Tên kiểu làm tham số thay vì kiểu thực sự ◦ Định nghĩa chính xác được quyết định ở thời điểm chạy Nhắc lại hàm swapValues: void swapValues(int& var1, int& var2) { int temp; temp = var1; var1 = var2; var2 = temp; } ◦ Chỉ áp dụng cho các biến kiểu int ◦ Nhưng phần mã lệnh làm việc với bất kỳ kiểu nào 3
- Khuôn mẫu hàm vs. Nạp chồng Có thể nạp chồng hàm cho kiểu char: void swapValues(char& var1, char& var2) { char temp; temp = var1; var1 = var2; var2 = temp; } Lưu ý: Mã lệnh gần giống nhau ◦ Chỉ khác nhau về kiểu được sử dụng ở 3 vị trí 4
- Cú pháp khuôn mẫu hàm Cho phép “hoán đổi giá trị” cho bất kỳ kiểu biến nào: template void swapValues(T& var1, T& var2) { T temp; temp = var1; var1 = var2; var2 = temp; } Dòng đầu tiên là tiền tố khuôn mẫu: ◦ Báo cho bộ biên dịch biết đằng sau là khuôn mẫu ◦ Và T là một tham số kiểu 5
- Tiền tố khuôn mẫu template Ở đây, class nghĩa là kiểu, hoặc sự phân lớp Dễ bị nhầm lẫn với từ class được sử dụng rộng rãi ◦ C++ cho phép sử dụng từ khóa “typename” ở vị trí từ khóa class ◦ Tuy nhiên trong mọi trường hợp nên sử dụng class T có thể được thay bằng bất kỳ kiểu nào ◦ Kiểu được định nghĩa trước hoặc người dùng định nghĩa Trong thân định nghĩa hàm ◦ T được sử dụng giống như một kiểu bất kỳ 6
- Định nghĩa khuôn mẫu hàm Khuôn mẫu hàm swapValues() thực sự là một tập hợp các định nghĩa ◦ Một định nghĩa cho mỗi kiểu có thể có Bộ biên dịch chỉ phát sinh các định nghĩa khi được yêu cầu ◦ Với điều kiện bạn đã định nghĩa cho tất cả các kiểu Viết một định nghĩa làm việc cho tất cả các kiểu có thể có 7
- Gọi khuôn mẫu hàm Xét lời gọi hàm sau đây swapValues(int1, int2); ◦ Bộ biên dịch C++ sử dụng khuôn mẫu để khởi tạo định nghĩa hàm cho hai tham số int Tương tự như tất cả các kiểu khác Không cần làm điều gì đặc biệt trong lời gọi ◦ Định nghĩa cần thiết được phát sinh tự động 8
- Một khuôn mẫu hàm khác Khai báo/nguyên mẫu: template void showStuff(int stuff1, T stuff2, T stuff3); Định nghĩa template void showStuff(int stuff1, T stuff2, T stuff3) { cout << stuff1 << endl << stuff2 << endl << stuff3 << endl; } 9
- Lời gọi showStuff Xét lời gọi hàm: showStuff(2, 3.3, 4.4); Bộ biên dịch phát sinh định nghĩa hàm ◦ Thay T bằng double ◦ Vì tham số thứ hai có kiểu double Hiển thị: 2 3.3 4.4 10
- Sự biên dịch Khai báo và định nghĩa hàm ◦ Chúng ta thường tách rời chúng ◦ Với các khuôn mẫu việc này không được hỗ trợ trong hầu hết các bộ biên dịch An toàn nhất là đặt định nghĩa hàm khuôn mẫu trong file mà nó được gọi ◦ Nhiều bộ biên dịch yêu cầu nó xuất hiện ở vị trí đầu tiên ◦ Chúng ta thường #include tất các các định nghĩa khuôn mẫu 11
- Khuôn mẫu đa tham số kiểu Có thể có: template Không đặc thù: ◦ Thường chỉ cần một kiểu có thể thay thế ◦ Không cho phép có tham số khuôn mẫu không được sử dụng Mỗi tham số khuôn mẫu cần được sử dụng trong định nghĩa Bằng không chương trình dịch sẽ báo lỗi 12
- Trừu tượng hóa thuật toán Liên quan đến việc thi hành khuôn mẫu Biểu diễn thuật toán theo cách chung nhất: ◦ Thuật toán áp dụng cho các biến thuộc bất kỳ kiểu nào ◦ Bỏ qua chi tiết không thiết yếu ◦ Tập trung vào các phần trọng yếu của thuật toán Khuôn mẫu hàm là một cách C++ hỗ trợ trừu tượng hóa thuật toán 13
- Chiến lược định nghĩa khuôn mẫu Phát triển hàm như thông thường ◦ Sử dụng các kiểu dữ liệu thật Hoàn thành việc gỡ lỗi hàm nguyên bản Sau đó chuyển đổi thành khuôn mẫu ◦ Thay thế các tên kiểu bằng tham số kiểu khi cần Ưu điểm: ◦ Dễ giải quyết trường hợp cụ thể ◦ Tập trung vào thuật toán, thay vì cú pháp khuôn mẫu 14
- Các kiểu không phù hợp trong khuôn mẫu Có thể sử dụng bất kỳ kiểu nào trong khuôn mẫu làm cho mã lệnh có nghĩa ◦ Mã lệnh phải vận hành theo cách phù hợp Ví dụ, hàm khuôn mẫu swapValues() ◦ Không thể sử dụng kiểu mà toán tử gán chưa được định nghĩa cho nó ◦ Ví dụ: một mảng: int a[10], b[10]; swapValues(a, b); ◦ Các mảng không được phép gán 15
- Khuôn mẫu lớp Cũng có thể “khái quát hóa” các lớp template ◦ Có thể áp dụng cho định nghĩa lớp ◦ Tất cả các bản thể của T trong định nghĩa lớp được thay thế bằng tham số kiểu ◦ Giống như với các khuôn mẫu hàm Một khi khuôn mẫu được định nghĩa, có thể khai báo các đối tượng của lớp 16
- Định nghĩa khuôn mẫu lớp template class Pair { public: Pair(); Pair(T firstVal, T secondVal); void setFirst(T newVal); void setSecond(T newVal); T getFirst() const; T getSecond() const; private: T first; T second; }; 17
- Các thành viên lớp khuôn mẫu Pair template Pair ::Pair(T firstVal, T secondVal) { first = firstVal; second = secondVal; } template void Pair ::setFirst(T newVal) { first = newVal; } 18
- Lớp khuôn mẫu Pair Các đối tượng của lớp có “cặp” giá trị kiểu T Sau đó có thể khai báo các đối tượng: Pair score; Pair seats; ◦ Sau đó có thể sử dụng các đối tượng giống như các đối tượng bất kỳ Ví dụ sử dụng: score.setFirst(3); score.setSecond(0); 19
- Định nghĩa hàm thành viên Pair Lưu ý trong định nghĩa hàm thành viên: ◦ Bản thân mỗi định nghĩa là một khuôn mẫu ◦ Đòi hỏi tiền tố khuôn mẫu trước mỗi định nghĩa ◦ Tên lớp trước :: là Pair thay vì chỉ là Pair ◦ Nhưng tên hàm tạo chỉ là Pair ◦ Tên hàm hủy cũng chỉ là ~Pair 20
- Khuôn mẫu lớp làm tham số Xét: int addUP(const Pair & thePair); ◦ Kiểu (int) được cung cấp để sử dụng cho T trong định nghĩa tham số kiểu lớp này ◦ Ở đây xảy ra truyền tham chiếu Kiểu khuôn mẫu có thể được sử dụng bất cứ chỗ nào cho phép sử dụng các kiểu chuẩn 21
- Khuôn mẫu lớp trong khuôn mẫu hàm Thay vì định nghĩa nạp chồng mới: template T addUp(const Pair & thePair); //Tiền điều kiện: Toán tử + được định nghĩa cho các giá trị kiểu T //Trả về tổng của hai giá trị trong thePair Hàm bây giờ áp dụng cho tất cả các kiểu số 22
- Các hạn chế trên tham số kiểu Chỉ các kiểu hợp lý có thể thay thế cho T Xét: ◦ Toán tử gán phải hoạt động tốt ◦ Hàm tạo sao chép cũng phải hoạt động tốt ◦ Nếu T bao gồm con trỏ thì hàm hủy phải phù hợp Các vấn đề tương tự như khuôn mẫu hàm 23
- Các định nghĩa kiểu Có thể định nghĩa tên kiểu lớp mới ◦ Để biểu diễn tên khuôn mẫu lớp được đặc tả Ví dụ: typedef Pair PairOfInt; Tên “PairOfInt” bây giờ được sử dụng để khai báo các đối tượng kiểu Pair : PairOfInt pair1, pair2; Tên cũng có thể được sử dụng làm tham số, hoặc ở bất kỳ chỗ nào cho phép các tên kiểu 24
- Hàm bạn và Khuôn mẫu Hàm bạn có thể được sử dụng với các lớp khuôn mẫu ◦ Giống như các lớp nguyên bản ◦ Chỉ đòi hỏi tham số kiểu ở vị trí phù hợp Việc khuôn mẫu lớp có hàm bạn là rất phổ biến ◦ Đặc biệt trong việc nạp chồng toán tử 25
- Lớp khuôn mẫu định nghĩa trước Lớp vector là một lớp khuôn mẫu Một ví dụ khác: Lớp khuôn mẫu basic_string ◦ Xử lý các chuỗi phần tử có kiểu bất kỳ ◦ Ví dụ: basic_string làm việc với kiểu char basic_string làm việc với kiểu double basic_string làm việc với các đối tượng YourClass ◦ string là tên thay thế của basic_string ◦ basic_string được định nghĩa trong thư viện 26
- Khuôn mẫu và kế thừa Các lớp khuôn mẫu dẫn xuất ◦ Có thể dẫn xuất từ lớp khuôn mẫu hoặc không khuôn mẫu ◦ Lớp dẫn xuất sau đó về bản chất là một lớp khuôn mẫu Cú pháp tương tự như lớp nguyên bản được dẫn xuất từ lớp nguyên bản 27
- Tóm tắt Khuôn mẫu hàm ◦ Định nghĩa các hàm với tham số của một kiểu Khuôn mẫu lớp ◦ Định nghĩa lớp với tham số của các phần con của lớp Các lớp có sẵn vector và basic_string là các lớp khuôn mẫu Có thể định nghĩa lớp khuôn mẫu được dẫn xuất từ một lớp cơ sở khuôn mẫu 28