Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 38 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
38
Dung lượng
328,5 KB
Nội dung
CHƯƠNG 6: Template (Khuôn mẫu) Bộ môn Công nghệ Phần mềm Khoa Công Nghệ Thông Tin Đại học Bách khoa – Đại học Đà Nẵng Nội dung Lập trình tổng quát (generic programming) Lập trình tổng quát C C++ template Khuôn mẫu hàm Khuôn mẫu lớp Các tham số template khác Template sử dụng template Lập trình tổng quát Lập trình tổng quát phương pháp lập trình độc lập với chi tiết biểu diễn liệu Tư tưởng ta định nghĩa khái niệm không phụ thuộc biểu diễn cụ thể nào, sau kiểu liệu thích hợp làm tham số Lập trình tổng quát Ta quen với ý tưởng có phương thức định nghĩa cho sử dụng với lớp khác nhau, đáp ứng cách thích hợp Khi nói đa hình, phương thức "draw" gọi cho đối tượng thừa kế Shape, định nghĩa tương ứng gọi để đối tượng vẽ Trong trường hợp này, hình đòi hỏi định nghĩa phương thức khác để đảm bảo vẽ hình Nhưng định nghĩa hàm cho kiểu liệu khác không cần phải khác sao? Lập trình tổng quát Ví dụ, xét hàm sau: void swap(int& a, int& b) { int temp; temp = a; a = b; b = temp; } Hàm cần hoán đổi giá trị chứa hai biến int Nếu ta muốn thực việc tương tự cho kiểu liệu khác, chẳng hạn float? Có thực cần đến hai phiên không? void swap(float& a, float& b) { float temp; temp = a; a = b; b = temp; } class Stack { public: Stack(); ~Stack(); Ví dụ khác: ta định nghĩa void push(const int& i); void pop(int& i); lớp biểu diễn cấu trúc bool isEmpty() const; ngăn xếp cho kiểu int private: int *data; Ta thấy khai báo định nghĩa Stack phụ thuộc}; Lập trình tổng quát mức độ vào kiểu liệu int Một số phương thức lấy tham số trả kiểu int Nếu ta muốn tạo ngăn xếp cho kiểu liệu khác sao? Ta có nên định nghĩa lại hoàn toàn lớp Stack (kết tạo nhiều lớp chẳng hạn IntStack, FloatStack, …) hay không? Lập trình tổng quát Ta thấy, số trường hợp, đưa chi tiết kiểu liệu vào định nghĩa hàm lớp điều lợi Thực ra, khái niệm lập trình tổng quát học theo sử dụng phương pháp lớp sở cho thể lớp dẫn xuất Trong ta cần định nghĩa khác cho "draw" Point hay Circle, vấn đề khác hẳn với trường hợp hàm có nhiệm vụ hoán đổi hai giá trị Ví dụ, thừa kế khỉ, ta muốn phương thức draw() thực thi, trỏ/tham chiếu tới Point hay Circle Với lập trình tổng quát, ta tìm cách mở rộng trừu tượng hoá địa hạt thừa kế Lập trình tổng quát C Sử dụng trình tiền xử lý C Trình tiền xử lý thực thay text trước dịch Do đó, ta dùng #define để kiểu liệu thay đổi chỗ cần #define TYPE int void swap(TYPE & a, TYPE & b) { TYPE temp; temp = a; a = b; b = temp; } Trình tiền xử lý thay "TYPE" "int" trước thực biên dịch Hai hạn chế: nhàm chán dễ lỗi cho phép định nghĩa chương trình C++ template Template (khuôn mẫu) chế thay mã cho phép tạo cấu trúc mà rõ kiểu liệu Từ khoá template dùng C++ để báo cho trình biên dịch đoạn mã theo sau thao tác nhiều kiểu liệu chưa xác định Từ khoá template theo sau cặp ngoặc nhọn chứa tên kiểu liệu tuỳ ý cung cấp Chú ý: template // Declaration that makes reference to a data type "T" Một lệnh template có hiệu đối template // Declaration that makes reference to a data type "T" với khai báo sau // and a datatype "U" C++ template Hai loại khuôn mẫu bản: Function template – khuôn mẫu hàm cho phép định nghĩa hàm tổng quát dùng đến kiểu liệu tuỳ ý Class template – khuôn mẫu lớp cho phép định nghĩa lớp tổng quát dùng đến kiểu liệu tuỳ ý Ta mô tả loại trước bàn đến phức tạp lập trình khuôn mẫu Stack cho số nguyên Khai báo định nghĩa lớp Stack cho kiểu int Bắt đầu ngăn xếp đơn giản class Stack { public: Stack(); ~Stack(); void push(const int& i) throw (logic_error); void pop(int& i) throw (logic_error); bool isEmpty() const; bool isFull() const; private: static const int max = 10; int contents[max]; int current; }; Stack cho số nguyên Stack::Stack() { this->current = 0; } Stack::~Stack() {} void Stack::push(const int& i) throw(logic_error) { if (this->current < this->max) { this->contents[this->current++] = i; } else { throw logic_error(“Stack is full.”); } } void Stack::pop(int& i) throw(logic_error) { if (this->current > 0) { i = this->contents[ this->current]; } else { throw logic_error(“Stack is empty.”); } } bool Stack::isEmpty() const { return (this->current == 0;) } bool Stack::isFull() const { return (this->current == this->max); } Template Stack Chuyển khai báo định nghĩa trước thành phiên tổng quát: Thêm lệnh template để template nói phần kiểu typedef T mang[max] rõ sau template class Stack { public: Stack(); ~Stack(); void push(const T& i) throw (logic_error); void pop(T& i) throw (logic_error); bool isEmpty() const; bool isFull() const; private: static const int max = 10; Mang contents; //T contents[max]; int current; }; TemplateMỗiStack phương thức cần template Stack::Stack(){ this->current = 0; } template Stack::~Stack() {} lệnh template đặt trước Mỗi dùng toán tử phạm vi, cần ký hiệu ngoặc nhọn kèm theo tên kiểu Ta định nghĩa lớp Stack, không định nghĩa lớp Stack template void Stack::push(const T& i) { if (this->current < this->max) { Thay kiểu đối tượng this->contents[this->current++]=i; lưu ngăn xếp } (trước int) kiểu tuỳ else { ýT throw logic_error(“Stack is full.”); } } Template Stack template void Stack::pop(T& i) { if (this->current > 0) { i = this->contents[ this->current]; } else { throw logic_error(“Stack is empty.”); } } template bool Stack::isEmpty() const { return (this->current == 0;) } template bool Stack::isFull() const { return (this->current == this->max); } Template Stack Sau đó, ta tạo sử dụng thể lớp định nghĩa template ta: int x = 5, y; char c = 'a', d; Stack s; Stack t; s.push(x); t.push(c); s.pop(y); t.pop(d); Các tham số khuôn mẫu khác Ta nói đến lệnh template với tham số thuộc "kiểu" typename Tuy nhiên, có hai "kiểu" tham số khác Kiểu thực (ví dụ: int) Các template Các tham số khuôn mẫu khác Nhớ lại cài đặt Stack, ta có max quy định số lượng tối đa đối tượng mà ngăn xếp chứa Như vậy, thể có kích thước kiểu đối tượng chứa Nếu ta không muốn đòi hỏi Stack có kích thước tối đa nhau? Ta thêm tham số vào lệnh template số int (giá trị dùng để xác định giá trị cho max) template // Specifies that one arbitrary type T and one int I // will be parameters in the following statement Lưu ý: ta khai báo tham số int giống khai báo khác Các tham số khuôn mẫu khác Sửa khai báo định nghĩa trước để sử dụng tham số mới: template class Stack { Khai báo tham số public: Stack(); ~Stack(); void push(const T& i) throw (logic_error); void pop(T& i) throw (logic_error); bool isEmpty() const; Sử dụng tham số để bool isFull() const; xác định giá trị max private: lớp thuộc kiểu //static const int max = I; T contents[I]; int current; }; Các tham số khuôn mẫu khác template Stack::Stack() { this->current = 0; } Sửa tên lớp dùng cho toán tử phạm vi Sửa lệnh template template Stack::~Stack() {} template void Stack::push(const T& i) { if (this->current < this->max) { this->contents[this->current++] = i; } else { throw logic_error(“Stack is full.”); } } Các tham số khuôn mẫu khác Giờ ta tạo thể lớp Stack với kiểu liệu kích thước đa dạng Stack s; // Creates an instance of a Stack // class of ints with max = Stack t; // Creates an instance of a Stack // class of ints with max = 10 Stack u; // Creates an instance of a Stack // class of chars with max = Lưu ý lệnh tạo thể lớp khác Các tham số khuôn mẫu khác Các ràng buộc sử dụng kiểu thực làm tham số cho lệnh template: Chỉ dùng kiểu số nguyên, trỏ, tham chiếu Không gán trị cho tham số lấy địa tham số Các tham số khuôn mẫu khác Một loại tham số thứ ba cho lệnh template template Ví dụ, xét thiết kế khuôn mẫu cho lớp Map (ánh xạ) ánh xạ khoá tới giá trị Lớp cần lưu ánh xạ từ khoá tới giá trị, ta không muốn kiểu đối tượng lưu trữ từ đầu Ta tạo Map khuôn mẫu cho sử dụng kiểu khác cho khoá giá trị Tuy nhiên, ta cần lớp chứa (container) template, để lưu trữ khoá giá trị kiểu tuỳ ý Các tham số khuôn mẫu khác Ta khai báo lớp Map: template< typename K, typename V,template Container> class Map { private: Container keys; Container values; }; Sau tạo thể Map sau: Map< string, int, Stack> wordcount; Lệnh tạo thể lớp Map tập xạ từ tới đó) Ta dùng template Stack để làm container lưu trữ thông tin Các tham số khuôn mẫu khác Như vậy, trình biên dịch sinh khai báo định nghĩa thực cho lớp Map, đọc tham số mô tả thành viên liệu Khi đó, sử dụng khuôn mẫu Stack để sinh mã cho hai lớp Stack Stack Đến đây, ta phải hiểu rõ container phải khuôn mẫu, không, làm để dùng để tạo loại stack khác nhau?