Giáo trình C toàn tập
Chương Tái định nghĩa Chương thảo luận tái định nghĩa hàm toán tử C++ Thuật ngữ tái định nghĩa (overloading) nghĩa ‘cung cấp nhiều định nghĩa’ Tái định nghĩa hàm liên quan đến việc định nghĩa hàm riêng biệt chia sẻ tên, hàm có dấu hiệu Tái định nghĩa hàm thích hợp cho: Định nghĩa hàm chất làm công việc thao tác kiểu liệu khác • Cung cấp giao diện tới hàm • Tái định nghĩa hàm (function overloading) tiện lợi lập trình Giống hàm, toán tử nhận toán hạng (các đối số) trả giá trị Phần lớn tốn tử C++ có sẵn tái định nghĩa Ví dụ, tốn tử + sử dụng để cộng hai số nguyên, hai số thực, hai địa Vì thế, có nhiều định nghĩa khác Các định nghĩa xây dựng sẵn cho toán tử giới hạn kiểu có sẵn Các định nghĩa thêm vào cung cấp lập trình viên cho chúng thao tác kiểu người dùng định nghĩa Mỗi định nghĩa thêm vào cài đặt hàm Tái định nghĩa toán tử minh họa cách sử dụng số lớp đơn giản Chúng ta thảo luận qui luật chuyển kiểu sử dụng để rút gọn nhu cầu cho nhiều tái định nghĩa tốn tử Chúng ta trình bày ví dụ tái định nghĩa số tốn tử phổ biến gồm > cho xuất nhập, [] () cho lớp chứa, toán tử trỏ Chúng ta thảo luận việc khởi tạo gán tự động, tầm quan trọng việc cài đặt xác chúng lớp sử dụng thành viên liệu cấp phát động Khơng giống hàm tốn tử, lớp tái định nghĩa; lớp phải có tên Tuy nhiên, thấy chương 8, lớp sửa đổi mở rộng thông qua khả thừa kế (inheritance) Chương 8: Tái định nghĩa 122 8.1 Tái định nghĩa hàm Xem xét hàm, GetTime, trả thời gian ngày theo tham số nó, giả sử cần có hai biến thể hàm này: trả thời gian theo giây tính từ nửa đêm, trả thời gian theo giờ, phút, giây Rõ ràng hàm phục vụ mục đích nên khơng có lý lại chúng có tên khác C++ cho phép hàm tái định nghĩa, nghĩa hàm có định nghĩa: long GetTime (void); // số giây tính từ nửa đêm void GetTime (int &hours, int &minutes, int &seconds); Khi hàm GetTime gọi, trình biên dịch so sánh số lượng kiểu đối số lời gọi với định nghĩa hàm GetTime chọn khớp với lời gọi Ví dụ: int h, m, s; long t = GetTime(); GetTime(h, m, s); // khớp với GetTime(void) // khớp với GetTime(int&, int&, int&); Để tránh nhầm lẫn định nghĩa hàm tái định nghĩa phải có dấu hiệu Các hàm thành viên lớp tái định nghĩa: class Time { // long GetTime (void); // số giây tính từ nửa đêm void GetTime (int &hours, int &minutes, int &seconds); }; Tái định nghĩa hàm giúp ta thu nhiều phiên đa dạng hàm mà khơng thể có cách sử dụng đơn độc đối số mặc định Các hàm tái định nghĩa có đối số mặc định: void Error (int errCode, char *errMsg = ""); void Error (char *errMsg); 8.2 Tái định nghĩa toán tử C++ cho phép lập trình viên định nghĩa ý nghĩa thêm vào cho toán tử xác định trước cách tái định nghĩa chúng Ví dụ, tái định nghĩa tốn tử + – để cộng trừ đối tượng Point: class Point { public: Point (int x, int y) {Point::x = x; Point::y = y;} Chương 8: Tái định nghĩa 123 Point operator + (Point &p) {return Point(x + p.x,y + p.y);} Point operator - (Point &p) {return Point(x - p.x,y - p.y);} private: int x, y; }; Sau định nghĩa + – sử dụng để cộng trừ điểm giống chúng sử dụng để cộng trừ số: Point p1(10,20), p2(10,20); Point p3 = p1 + p2; Point p4 = p1 - p2; Việc tái định nghĩa toán tử + – sử dụng hàm thành viên Một khả khác tốn tử tái định nghĩa tồn cục: class Point { public: Point (int x, int y) {Point::x = x; Point::y = y;} friend Point operator + (Point &p, Point &q) {return Point(p.x + q.x,p.y + q.y);} friend Point operator - (Point &p, Point &q) {return Point(p.x - q.x,p.y - q.y);} private: int x, y; }; Sử dụng toán tử tái định nghĩa tương đương với lời gọi rõ ràng tới hàm thi công Ví dụ: operator+(p1, p2) // tương đương với: p1 + p2 Thơng thường, để định nghĩa tốn tử λ xác định trước định nghĩa hàm tên operator λ Nếu λ toán tử nhị hạng: • operator λ phải nhận xác đối số định nghĩa thành viên lớp, hai đối số định nghĩa tồn cục Tuy nhiên, λ tốn tử đơn hạng: • operator λ phải nhận khơng đối số định nghĩa thành viên lớp, đối số định nghĩa toàn cục Bảng 8.1 tổng kết tốn tử C++ tái định nghĩa Năm tốn tử cịn lại không tái định nghĩa là: Chương 8: Tái định nghĩa * :: ?: sizeof 124 Bảng 8.1 Các tốn tử tái định nghĩa + new + = * delete * += -= ! ~ & ++ () -> ->* / /= % %= & &= | |= ^ ^= == Đơn hạng - != > = & & || >> = () , Nhị hạng < Toán tử đơn hạng (ví dụ ~) khơng thể tái định nghĩa nhị hạng tốn tử nhị hạng (ví dụ =) khơng thể tái định nghĩa tốn tử đơn hạng C++ khơng hỗ trợ định nghĩa tốn tử new điều dẫn đến mơ hồ Hơn nữa, luật ưu tiên cho tốn tử xác định trước cố định khơng thể sửa đổi Ví dụ, bạn tái định nghĩa tốn tử * ln có độ ưu tiên cao tốn tử + Các tốn tử ++ –- tái định nghĩa tiền tố hậu tố Các luật tương đương không áp dụng cho tốn tử tái định nghĩa Ví dụ, tái định nghĩa + không ảnh hưởng tới += toán tử += tái định nghĩa rõ ràng Các tốn tử ->, =, [], () tái định nghĩa hàm thành viên, khơng tồn cục Để tránh chép đối tượng lớn truyền chúng tới toán tử tái định nghĩa tham chiếu nên sử dụng Các trỏ khơng thích hợp cho mục đích tốn tử tái định nghĩa khơng thể thao tác tồn trỏ Ví dụ: Các tốn tử tập hợp Lớp Set giới thiệu chương Phần lớn hàm thành viên Set định nghĩa toán tử tái định nghĩa tốt Danh sách 8.1 minh họa Chương 8: Tái định nghĩa 125 Danh sách 8.1 #include const maxCard = 100; enum Bool {false, true}; class Set { public: Set(void) { card = 0; } friend Bool operator & (const int, Set&); friend Bool operator == (Set&, Set&); friend Bool operator != (Set&, Set&); 10 friend Set operator * (Set&, Set&); 11 friend Set operator + (Set&, Set&); 12 // 13 void AddElem(const int elem); 14 void Copy (Set &set); 15 void Print (void); 16 private: 17 int elems[maxCard]; 18 int card; 19 }; // vien // bang // khong bang // giao // hop // cac phan tu cua tap hop // so phan tu cua tap hop Ở đây, phải định định nghĩa hàm thành viên tốn tử bạn tồn cục Chúng định nghĩa cách dễ dàng hàm thành viên Việc thi công hàm sau Bool operator & (const int elem, Set &set) { for (register i = 0; i < set.card; ++i) if (elem == set.elems[i]) return true; return false; } Bool operator == (Set &set1, Set &set2) { if (set1.card != set2.card) return false; for (register i = 0; i < set1.card; ++i) if (!(set1.elems[i] & set2)) // sử dụng & tái định nghĩa return false; return true; } Bool operator != (Set &set1, Set &set2) { return !(set1 == set2); // sử dụng == tái định nghĩa } Set operator * (Set &set1, Set &set2) { Set res; for (register i = 0; i < set1.card; ++i) if (set1.elems[i] & set2) // sử dụng & tái định nghĩa res.elems[res.card++] = set1.elems[i]; Chương 8: Tái định nghĩa 126 } return res; Set operator + (Set &set1, Set &set2) { Set res; } set1.Copy(res); for (register i = 0; i < set2.card; ++i) res.AddElem(set2.elems[i]); return res; Cú pháp để sử dụng toán tử ngắn gọn cú pháp hàm mà chúng thay minh họa hàm main sau: int main (void) { Set s1, s2, s3; s1.AddElem(10); s1.AddElem(20); s1.AddElem(30); s1.AddElem(40); s2.AddElem(30); s2.AddElem(50); s2.AddElem(10); s2.AddElem(60); cout = 1; } } Binary operator + (const Binary n1, const Binary n2) { unsigned carry = 0; Chương 8: Tái định nghĩa 131 unsigned value; Binary res = "0"; } for (register i = binSize - 1; i >= 0; i) { value = (n1.bits[i] == '0' ? : 1) + (n2.bits[i] == '0' ? : 1) + carry; res.bits[i] = (value % == ? '0' : '1'); carry = value >> 1; } return res; Binary::operator int () { unsigned value = 0; } for (register i = 0; i < binSize; ++i) value = (value