Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 32 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
32
Dung lượng
54,85 KB
Nội dung
Chương 4 Quá tải hàm • Quá tải hàm tạo • Hàm tạo bản sao • Hàm với các đối số mặc đònh • Tính không xác đònh khi quá tải hàm • Điạ chỉ của hàm quá tải Chöông 4 Quaù taûi haøm 104 104 Chương 4 Quá tải hàm 105 105 I/ Quá tải hàm tạo ( constructor overloading ) Có thể quá tải hàm tạo của một lớp, nhưng không quá tải hàm hủy. Hàm tạo của lớp phải phù hợp với cách mà đối tượng của lớp đó được khai báo. Nếu không lỗi thời gian biên dòch sẽ xảy ra. Có 3 lý do cần quá tải hàm tạo : + để có tính linh hoạt + để hổ trợ mảng + để tạo các hàm tạo bản sao Hạn chế : nếu thực hiện quá tải nhiều lần có thể tạo ra tác dụng hủy hoại trên lớp. • Quá tải hàm tạo với khởi đầu một đối tượng hoặc không khởi đầu đối tượng Ví dụ 1.1 #include <iostream.h> class myclass { int x; public: // overload constructor two ways myclass() { x = 0; } // no initializer myclass(int n) { x = n; } // initializer int getx() { return x; } }; int main() { myclass o1(10) ; // declare with initial value myclass o2 ; // declare without initializer cout << "o1: " << o1.getx() << '\n'; cout << "o2: " << o2.getx() << '\n'; return 0; } Chương 4 Quá tải hàm 106 106 • Quá tải hàm tạo để cho các đối tượng riêng lẽ lẫn các mảng đối tượng xảy ra trong chương trình. Ví dụ 1.2 #include <iostream.h> class myclass { int x; public: // overload constructor two ways myclass() { x = 0; } // no initializer myclass(int n) { x = n; } // initializer int getx() { return x; } }; int main() { myclass o1[10] ; // declare array without initializers myclass o2[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ; // declare with initializers int i; for(i=0; i<10; i++) { cout << "o1[" << i << "]: " << o1[i].getx(); cout << '\n'; cout << "o2[" << i << "]: " << o2[i].getx(); cout << '\n'; } return 0; } • Quá tải hàm tạo giúp lập trình viên chọn phương pháp thuận lợi nhất để khởi đầu một đối tượng. Chửụng 4 Quaự taỷi haứm 107 107 Vớ duù 1.3 #include <iostream.h> #include <stdio.h> // included for sscanf() class date { int day, month, year; public: date(char *str) ; date (int m, int d, int y) { day = d; month = m; year = y; } void show() { cout << month << '/' << day << '/'; cout << year << '\n'; } }; date::date(char *str) { sscanf(str, "%d%*c%d%*c%d", &month, &day, &year); } int main() { // construct date object using string date sdate("12/31/99") ; // construct date object using integers date idate(12, 31, 99) ; sdate.show(); idate.show(); return 0; } Chương 4 Quá tải hàm 108 108 • Quá tải hàm tạo khi mảng động của lớp được cấp phát. Điều này giúp giải quyết hạn chế trong chương 3, phần IV/2, một mảng động không thể được khởi đầu, xem ví dụ 4.6. Ví dụ 1.4 #include <iostream.h> class myclass { int x; public: // overload constructor two ways myclass() { x = 0; } // no initializer myclass(int n) { x = n; } // initializer int getx() { return x; } void setx(int n) { x = n; } }; int main() { myclass *p; myclass ob(10); // initialize single variable p = new myclass[10]; // can't use initializers here if(!p) { cout << "Allocation error\n"; return 1; } int i; // initialize all elements to ob for(i=0; i<10; i++) p[i] = ob ; for(i=0; i<10; i++) { cout << "p[" << i << "]: " << p[i].getx(); cout << '\n'; } Chương 4 Quá tải hàm 109 109 return 0; } Bài tập I 1. Cho lớp sau, hãy bổ sung hai hàm tạo. Hàm tạo thứ nhất không nhận tham số, hàm này dùng toán tử new cấp phát 255 bytes bộ nhớ, khởi đầu bộ nhớ như một chuỗi rỗng và cho len = 255. Hàm tạo thứ hai nhận hai tham số, tham số thứ nhất là chuỗi dùng để khởi đầu và tham số kia là số byte để cấp phát. Cho phiên bản này cấp phát lượng bộ nhớ đã được chỉ rõ và chép chuổi vào bộ nhớ. Thực hiện việc kiểm tra giới hạn biên cần thiết và chứng tỏ các hàm tạo sẽ hoạt động qua một chương trình ngắn. class strtype { char *p; int len; public: char *getstring() { return p; } int getlength() { return len; } }; 2. Từ bài tập II, 2 chương 2, bổ sung hàm tạo không nhận tham số và một phiên bản được quá tải để nhận giờ hệ thống dưới dạng được trả về bởi hàm chuẩn clock(). II/ Tạo và sử dụng hàm tạo bản sao (copy constructor) 1/ Khái niệm Hàm tạo bản sao là kiểu đặc biệt của hàm tạo được quá tải. Khi một đối tượng được truyền cho một hàm, bản sao từng bit một của đối tượng đó được tạo ra và được truyền cho tham số của hàm để nhận đối tượng. Chương 4 Quá tải hàm 110 110 Tuy nhiên, có những trường hợp trong đó bản sao đồng nhất là như không mong muốn. Chẳng hạn, nếu đối tượng có con trỏ tới bộ nhớ được cấp phát, thì bản sao sẽ trỏ tới cùng bộ nhớ như đối tượng gốc đã làm. Do đó, nếu bản sao tạo ra sự thay đổi cho nội dung bộ nhớ thì nó cũng sẽ được thay đổi đối với đối tượng gốc. Khi một hàm kết thúc, bản sao sẽ bò hủy và hàm hủy của nó được gọi. Điều này dẫn đến những tác dụng không mong muốn làm ảnh hưởng đến đối tượng gốc. Khi một đối tượng được trả về từ một hàm tình trạng tương tự cũng sẽ xảy ra. Trình biên dòch sẽ tạo ra một đối tượng tạm để giữ bản sao của giá trò do hàm trả về. Đối tượng tạm này sẽ ra khỏi phạm vi một khi giá trò được trả về cho thủ tục gọi, khiến hàm hủy của đối tượng tạm được gọi. Nếu hàm hủy hủy bỏ thứ gì cần cho thủ tục gọi, chẳng hạn nó giải phóng bộ nhớ cấp phát động, thì rắc rối sẽ xảy ra. Như vậy, cốt lõi của vấn đề trên là bản sao từng bit của đối tượng được tạo ra và thực hiện . Để ngăn chặn vấn đề này, lập trình viên cần xác đònh chính xác những gì xảy ra khi bản sao của một đối tượng được thực hiện để tránh được những tác dụng không mong muốn. Hàm tạo bản sao sẽ giải quyết được vấn đề trên. Khi đònh nghóa hàm tạo bản sao, lập trình viên có thể hoàn toàn kiểm soát chính xác những gì xảy ra khi bản sao của một đối tượng được thực hiện. • Cần phân biệt hai trường hợp trong đó giá trò của một đối tượng được truyền cho đối tượng khác : + Trường hợp thứ nhất là phép gán . + Trường hợp thứ hai là sự khởi đầu, có thể xảy ra theo 3 cách : - Khi một đối tượng được dùng để khởi đầu một đối tượng khác trong câu lệnh khai báo. - Khi một đối tượng được truyền như tham số cho hàm. - Khi một đối tượng tạm được tạo ra dùng để làm giá trò trả về bởi một hàm. Hàm tạo bản sao chỉ áp dụng cho sự khởi đầu . Nó không áp dụng cho phép gán. Hàm tạo bản sao không ảnh hưởng đến các phép gán. Chương 4 Quá tải hàm 111 111 2/ Cú pháp • Dạng tổng quát của hàm tạo bản sao classname( const classname & obj ) { // body of constructor } obj là một tham chiếu tới một đối tượng được dùng để khởi đầu một đối tượng khác • Dạng mở rộng của hàm tạo bản sao (có nhiều đối số) classname( const classname & obj, int x = 0 ) { // body of constructor } Đối số thứ nhất là một tham chiếu tới đối tượng được sao chép, và các đối tượng khác đều mặc đònh. Tính linh hoạt này cho phép tạo ra các hàm tạo bản sao có những công dụng khác. Ví dụ , lớp được gọi là myclass và y là đối tượng của myclass thì các lệnh sau đây sẽ dùng đến hàm tạo bản sao của myclass : myclass x = y; // y explicitly initializing x func1(y); // y passed as a parameter y = func2(); // y receiving a returned object Trong hai trường hợp đầu, một tham chiếu tới y sẽ được truyền cho hàm tạo bản sao. Trường hợp thứ ba, một tham chiếu tới đối tượng được trả về bởi func2() sẽ được truyền cho hàm tạo bản sao. • Tạo một mảng số nguyên "an toàn" có kiểm tra giới hạn biên Ví dụ 2.1 Chöông 4 Quaù taûi haøm 112 112 /* This program creates a "safe" array class. Since space for the array is dynamically allocated, a copy constructor is provided to allocate memory when one array object is used to initialize another. */ #include <iostream.h> #include <stdlib.h> class array { int *p; int size; public: array(int sz) { // constructor p = new int[sz]; if(!p) exit(1); size = sz; cout << "Using 'normal' constructor\n"; } ~array() {delete [] p;} // copy constructor array(const array &a) ; void put(int i, int j) { if(i>=0 && i<size) p[i] = j; } int get(int i) { return p[i]; } }; /* Copy constructor. In the following, memory is allocated specifically for the copy, and the address of this memory is assigned to p. Therefore, p is not pointing to the same dynamically allocated memory as the original object. */ array::array(const array &a) { [...]... cung cấp đối số mặc đònh khi không được gọi sẽ làm hỏng chương trình và sẽ hướng dẫn sai cho người khác sử dụng hàm đó Với sự quá tải hàm, một lập trình viên thành thạo sẽ biết được khi nào thì sử dụng đối số mặc đònh và khi nào không 1 24 Chương 4 Quá tải hàm 125 Bài tập III 1 Tìm lỗi sai trong nguyên mẫu hàm sau : char *f(char *p, int x = 0 , char *q) ; 2 Tìm lỗi sai trong nguyên mẫu dùng đối số mặc... dòch Bài tập V 1 Cho hai hàm quá tải Tìm điạ chỉ của mỗi hàm int dif(int a, int b) { return a-b; } float dif(float a, float b) { return a-b; } 131 Chương 4 Quá tải hàm 132 Bài tập chương 4 1 Hãy quá tải hàm tạo date() trong ví dụ 1.3, chương 4 để cho nó nhận một tham số kiểu time_t 2 Tìm lỗi sai trong chương trình sau : class samp { int a; public: samp(int i) { a = i; } // }: // int main() { samp... noninitialized 10-element array // } 8 Thực hiện quá tải hàm tạo đối với lớp sau đây sao cho các đối tượng không được khởi đầu cũng được tạo ra class myclass { int x, y; public : myclass(int i, int j) {x= i; y =j;} // } 9 Qua bài tập 8, hãy chứng tỏ có thể không quá tải myclass() bằng cách dùng đối số mặc đònh 133 Chương 4 Quá tải hàm 10 Cho lớp sau đây, có thể cấp phát động một mảng các đối tượng này không... đònh để báo cho reverse() đảo ngược toàn bộ chuỗi 4 Tìm lỗi sai trong đoạn chương trình sau : void compute(double *num, int divisor=1); void compute(double *num); // compute(&x); 5 Hãy tạo hàm order() để nhận hai tham số tham chiếu nguyên Nếu đối số thứ nhất lớn hơn đối số thứ hai, hãy đảo ngược hai đối số Ngược lại, không tác động nào Ví dụ 132 Chương 4 Quá tải hàm 133 int x=1, y=0; order(x, y); 6 Tại... " . '
'; return 0; } Chương 4 Quá tải hàm 106 106 • Quá tải hàm tạo để cho các đối tượng riêng lẽ lẫn các mảng đối tượng xảy ra trong chương trình. Ví dụ 1.2 #include <iostream.h>. được quá tải. Khi một đối tượng được truyền cho một hàm, bản sao từng bit một của đối tượng đó được tạo ra và được truyền cho tham số của hàm để nhận đối tượng. Chương 4 Quá tải hàm 110. đổi đối với đối tượng gốc. Khi một hàm kết thúc, bản sao sẽ bò hủy và hàm hủy của nó được gọi. Điều này dẫn đến những tác dụng không mong muốn làm ảnh hưởng đến đối tượng gốc. Khi một đối tượng