Chào cả nhà, lại là mình đây. Trong bài tiếp theo chúng ta sẽ cùng tìm hiểu về Nạp chồng toán tử và Nạp chồng hàm là gì nhé.
Ngôn ngữ C++ cho phép bạn xác định nhiều hơn một định nghĩa cho một tên hàm hoặc một toán tử trong cùng phạm vi (scope), được gọi tương ứng là Nạp chồng hàm (function overloading) và Nạp chồng toán tử (operator overloading).
Một khai báo nạp chồng là một khai báo mà đã được khai báo với cùng tên như một khai báo được khai báo trước đó trong cùng phạm vi, ngoại trừ rằng: cả hai khai báo có các tham số khác nhau và định nghĩa khác nhau.
Khi bạn gọi một hàm nạp chồng hoặc một toán tử nạp chồng, thì trình biên dịch quyết định định nghĩa thích hợp nhất để sử dụng bằng việc so sánh các kiểu tham số bạn đã sử dụng để gọi hàm hoặc toán tử với các kiểu tham số đã được xác định trong các định nghĩa. Tiến trình lựa chọn hàm nạp chồng hoặc toán tử nạp chồng thích hợp nhất này được gọi là phân giải nạp chồng ( overload resolution ).
Nạp Chồng Hàm
Đặt vấn đề
#include <bits/stdc++.h> using namespace std; int int_max(int a, int b) { if (a > b) return a; else return b; } int main() { cout << "int max = " << int_max(4, 5); return 0; }
Trong ví dụ trên, sau khi chạy chương trình ta nhận được kết quả
int max = 5
Nhưng nếu ta thay int_max(4,5)
thành int_max(4.4,5.5)
thì kết quả nhận được vẫn là int max = 5
giống phía trên, do khi khao báo hàm ta truyền vào 2 tham số thuộc kiểu int
. Vậy để có kết quả đúng là 5.5
thì ta phải viết thêm một hàm mới.
/* Code by KingNNT */ #include <bits/stdc++.h> using namespace std; int int_max(int a, int b) { if (a > b) return a; else return b; } int float_max(float a, float b) { if (a > b) return a; else return b; } int main() { cout << "int max = " << int_max(4, 5) << endl; cout << "float max = " << float_max(4.4, 5.5) << endl; return 0; }
Như vậy, ta sẽ có nhiều hàm với các tên gọi khác nhau. Việc sử dụng tên như vậy sẽ gây bất lợi cho người lập trình khi gọi hàm. Nạp chồng hàm ra đời để giải quyết vấn đề trên.
Nạp chồng hàm
Nạp chồng hàm (Function Overloading) là một kiến thức khá mới mẻ đối với các bạn mới bắt đầu làm quen với C++. Bởi vì kiến thức này không hề tồn tại trong C mà chỉ có ở C++.
Kỹ thuật này cho phép sử dụng cùng một tên gọi cho các hàm “giống nhau” (có cùng mục đích). Nhưng khác nhau về kiểu dữ liệu tham số hoặc số lượng tham số.
Quay về với ví dụ trên
Nạp chồng hàm cho phép ta khai báo và định nghĩa các hàm trên cùng với một tên gọi.
#include <bits/stdc++.h> using namespace std; int max(int a, int b) { if (a > b) return a; else return b; } int max(float a, float b) { if (a > b) return a; else return b; } int main() { cout << "int max = " << max(4, 5) << endl; cout << "float max = " << max(4.4, 5.5) << endl; return 0; }
Sau khi chạy ta có kết quả
int max = 5 float max = 5.5
Nạp Chồng Toán Tử
Giới thiệu về nạp chồng toán tử
Nạp chồng toán tử (Operator Overloading) được dùng để định nghĩa toán tử cho có sẵn trong c++ phục vụ cho dữ liệu riêng do bạn tạo ra.
Giả sử có lớp PhanSo
và có các phương thức tính toán như Cong, Tru, Nhan, Chia.
Nếu gặp một biểu thức phức tạp, số lượng phép tính nhiều thì việc sử dụng các phương thức trên khá khó khăn và có thể gây rối cho người lập trình. Vì thế ta sẽ nạp chồng lại các toán tử để có thể tạo một cái nhìn trực quan vào code, giảm thiểu các lỗi sai không đáng có.
Các loại toán tử
- C++ chỉ cho phép người dùng overloading lại các toán tử có sẵn trong c++
- Một toán tử có thể được định nghĩa cho nhiều kiểu dữ liệu khác nhau.
Toán tử đơn
Toán tử đơn là toán tử một ngôi (unary operator), có thể được dùng làm toán tử trước (prefix operator) và toán tử sau (postfix operator). Ví dụ phép tăng (++) hay phép giảm (–-)
Ví dụ:
- prefix operator: ++i;
- postfix operator: i++;
Toán tử đôi
Toán tử đôi là toán tử có 2 ngôi (binary operator).
Ví dụ: như A+B, A*B, hay toán tử chỉ mục […]
cũng là toán tử đôi.
Các toán tử có thể nạp chồng
Nạp chồng toán tử
Nạp chồng toán tử 1 ngôi
#include <bits/stdc++.h> using namespace std; class phanso { private: int tu, mau; public: phanso() { tu = mau = 0; } ~phanso() { tu = mau = 0; } void input() { cout << "Nhap tu so: "; cin >> this->tu; cout << "Nhap mau so: "; cin >> this->mau; } void output() { cout << this->tu << "/" << this->mau << endl; } phanso operator +(phanso b) { phanso c; c.tu = this->tu * b.mau + this->mau * b.tu; c.mau = this->mau * b.mau; return c; } }; int main() { phanso a, b, c; a.input(); b.input(); c = a + b; c.output(); }
Ta truyền Input:
1 2 3 4
Sau khi chạy chương trình ta có kết quả:
10/8
Trong phần code trên, mình đã nạp chồng toán tử +
cho lớp phân số bằng cách nạp chồng toán tử 1 ngôi ( chỉ có thể truyền vào một tham số )
Với cách nạp chồng này ta có thể coi nó là một phương thức của lớp:
- Tên phương thức sẽ có dạng
operator @
– trong đó@
là toán tử cần nạp chồng
Nạp chồng toán tử 2 ngôi
/* Code by KingNNT */ #include <bits/stdc++.h> using namespace std; class phanso { private: int tu, mau; public: phanso() { tu = mau = 0; } ~phanso() { tu = mau = 0; } void input() { cout << "Nhap tu so: "; cin >> this->tu; cout << "Nhap mau so: "; cin >> this->mau; } void output() { cout << this->tu << "/" << this->mau << endl; } friend phanso operator +(phanso a, phanso b) { phanso c; c.tu = a.tu * b.mau + a.mau * b.tu; c.mau = a.mau * b.mau; return c; } }; int main() { phanso a, b, c; a.input(); b.input(); c = a + b; c.output(); }
Vẫn với Input như trên
1 2 3 4
Sau khi biên dịch và chạy chương trình, kết quả vẫn nhận được là:
10/8
Với cách nạp chồng toán tử 2 ngôi này, thì hàm nạp chồng được coi là một hàm bạn của lớp:
- Có từ khoá
friend
ở đầu - Tên hàm là
operator @
– Trong đó@
vẫn là toán tử cần nạp chồng - 2 tham số được truyền vào là 2 giá trị thực hiện phép toán
Nếu không nhớ Hàm Bạn là gì? Bạn có thể xem lại: Tại Đây
Nạp chồng toán tử nhập, xuất
Việc nạp chồng toán tử nhập xuất cho phép người dùng dùng cin
, cout
nhập xuất nhanh một đối tượng mà không cần gọi lại cin
,cout
cho từng thuộc tính của dữ liệu dựa trên việc được định nghĩa trước.
Đối với nạp chồng toán tử nhập >>
và toán tử xuất <<
ta sẽ dùng cách nạp chồng toán tử 2 ngôi.
#include <bits/stdc++.h> using namespace std; class phanso { private: int tu, mau; public: phanso() { tu = mau = 0; } ~phanso() { tu = mau = 0; } friend istream &operator>>(istream &is, phanso &obj) { cout << "Nhap tu so: "; is >> obj.tu; cout << "Nhap mau so: "; is >> obj.mau; return is; } friend ostream &operator<<(ostream &os, phanso obj) { os << obj.tu << "/" << obj.mau << endl; return os; } }; int main() { phanso a; cin >> a; cout << a; }
Input:
1 2
Output:
1/2
Đối với toán tử nhập >>
kiểu trả về của nó sẽ là istream
. Trong phần code trên, mình truyền tham số đầu tiền là tham chiếu is
. Sau đó phần code trong thân hàm, tất cả cin
mình sẽ thay bằng is
và kết thúc hàm bằng return is
. Tham số thứ 2 được truyền vào cũng dưới dạng tham chiếu obj
thuộc lớp phanso
.
Cách thức nạp chồng toán tử xuất <<
Cũng tương tự như nạp chồng toán tử nhập >>
.
Điều khác biệt ở đây là os
có kiểu trả về là ostream
và tham số thứ 2 truyền vào có thể là tham trị chứ không nhất thiết phải là tham chiếu.
Bạn có thể xem tại Cách phân biệt Tham Chiếu và Tham Trị: Tại Đây
Nạp chồng toán tử gán
MyClass& MyClass::operator=(const MyClass &rhs) { if (this == &rhs) // kiểm tra có cùng đối tượng? return *this; // Nếu trùng thì bỏ qua và return chính nó //Xử lí... (Cấp phát vùng nhớ mới, sao chép giá trị, ...) return *this; }
Cú pháp và cách xử lý ở đây gần giống với Hàm khởi tạo sao chép
Bài viết của mình tới đây là hết rồi. Nếu có bất kì thắc mắc hay đóng góp nào, mọi người đừng ngần ngại comment ngay phía dưới nhé. Rất mong nhận được sự ủng hộ của các bạn. Xin chào và hẹn gặp lại.
Để lại một bình luận