Nạp chồng hàm
Thuộc phần Lập trình hướng đối tượng trong hành trình C++ của Coddy. Bài học 57 trên 104.
Nạp chồng hàm cho phép bạn định nghĩa nhiều hàm (function) cùng tên nhưng có danh sách tham số khác nhau. Trình biên dịch chọn phiên bản phù hợp dựa trên các đối số bạn truyền vào. Đây là một dạng đa hình quan trọng tại thời điểm biên dịch.
Để việc nạp chồng hoạt động, các hàm phải khác nhau về chữ ký: số lượng tham số, kiểu dữ liệu của chúng hoặc cả hai:
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
};
Calculator calc;
calc.add(5, 3); // Gọi add(int, int)
calc.add(2.5, 3.7); // Gọi add(double, double)
calc.add(1, 2, 3); // Gọi add(int, int, int)Trình biên dịch kiểm tra từng lời gọi và khớp lời gọi đó với hàm nạp chồng phù hợp. Việc này diễn ra hoàn toàn trong quá trình biên dịch, nên không phát sinh chi phí khi chạy chương trình.
Quan trọng: Chỉ riêng kiểu trả về không thể phân biệt các hàm nạp chồng. Đoạn code sau sẽ gây ra lỗi biên dịch:
int getValue();
double getValue(); // Lỗi: không thể nạp chồng chỉ dựa vào kiểu giá trị trả vềViệc nạp chồng hàm (function) giúp code của bạn trực quan hơn. Thay vì tạo các tên riêng như addInts, addDoubles và addThreeInts, bạn dùng một tên có ý nghĩa duy nhất và để trình biên dịch xử lý phần còn lại.
Thử thách
DễHãy xây dựng một lớp định dạng thông điệp để minh họa sức mạnh của nạp chồng hàm. Bạn sẽ tạo lớp Formatter với nhiều phương thức format() xử lý các kiểu đầu vào khác nhau, để trình biên dịch chọn phiên bản phù hợp dựa trên những gì bạn truyền vào.
Bạn sẽ tổ chức code trong hai tệp:
Formatter.h: Định nghĩa lớpFormattervới các phương thứcformat()được nạp chồng để xử lý các trường hợp khác nhau:- Một phương thức nhận một
std::stringvà in ra:Message: <text> - Một phương thức nhận một
std::stringvà một mức ưu tiênint, rồi in ra:[Priority <priority>] <text> - Một phương thức nhận một
std::stringvà một dấu thời giandouble, rồi in ra:[<timestamp>s] <text> - Một phương thức nhận ba tham số
std::string(người gửi, người nhận, thông điệp) và in ra:From <sender> to <receiver>: <message>
format(): trình biên dịch sẽ chọn phương thức phù hợp dựa trên các đối số.- Một phương thức nhận một
main.cpp: Đọc bốn đầu vào (mỗi đầu vào trên một dòng riêng):- Nội dung thông điệp đơn giản
- Mức ưu tiên (số nguyên)
- Dấu thời gian (số thực)
- Tên người gửi
Tạo một đối tượng
Formattervà minh họa cả bốn phương thức được nạp chồng:- Gọi
format()chỉ với nội dung thông điệp - Gọi
format()với nội dung thông điệp và mức ưu tiên - Gọi
format()với nội dung thông điệp và dấu thời gian - Gọi
format()với người gửi, "Admin" làm người nhận và nội dung thông điệp
Ví dụ, với các đầu vào System ready, 3, 12.5 và Server:
Message: System ready
[Priority 3] System ready
[12.5s] System ready
From Server to Admin: System readyHãy chú ý cách bạn gọi cùng một tên phương thức format() bốn lần, nhưng mỗi lần gọi sẽ thực thi một phiên bản khác nhau dựa trên kiểu và số lượng đối số. Đây là tính đa hình tại thời điểm biên dịch: trình biên dịch xác định phương thức nạp chồng nào cần dùng trước khi chương trình chạy.
Tự làm thử
#include <iostream>
#include <string>
#include "Formatter.h"
using namespace std;
int main() {
// Đọc các giá trị đầu vào
string message;
getline(cin, message);
int priority;
cin >> priority;
double timestamp;
cin >> timestamp;
cin.ignore();
string sender;
getline(cin, sender);
// TODO: Tạo một đối tượng Formatter
// TODO: Gọi format() chỉ với nội dung message
// TODO: Gọi format() với nội dung message và priority
// TODO: Gọi format() với nội dung message và timestamp
// TODO: Gọi format() với sender, "Admin" làm người nhận và nội dung message
return 0;
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng về OOP
Tệp bên ngoàiBiên dịch và tạo bản dựng C++Tệp header và tệp nguồnNamespace và phạm viGiới thiệu về OOP trong C++Lớp và đối tượngCon trỏ thisPhương thức (hàm thành viên)Thuộc tính (dữ liệu thành viên)Cơ bản về hàm khởi tạo và hàm hủyÔn tập: máy tính đơn giản4Thuộc tính của lớp
Thành viên đối tượng và thành viên tĩnhHàm truy xuất và hàm thiết lậpHàm thành viên hằngTừ khóa mutablePhương thức và biến tĩnhHàm và lớp bạnÔn tập: Trình quản lý tài khoản ngân hàng7Kế thừa
Kế thừa cơ bảnMức truy cập trong kế thừaThứ tự gọi hàm khởi tạo và hàm hủyGhi đè phương thứcHàm ảo và VTableĐa kế thừaKế thừa ảoÔn tập: hệ phân cấp nhân viên2Quản lý bộ nhớ
Bộ nhớ ngăn xếp và heapCon trỏ và tham chiếuBộ nhớ động (new/delete)Con trỏ thông minh trong C++RAII trong C++Ôn tập: Trình quản lý mảng động5Tính đóng gói
Phạm vi truy cập trong C++Tìm hiểu sâu về phạm vi truy cậpẨn thông tinStruct và lớpLớp lồng nhau và lớp bên trongÔn tập: hệ thống hồ sơ sinh viên8Đa hình
Đa hình lúc biên dịch và khi chạyNạp chồng hàmÔn lại hàm ảoHàm ảo thuần túyLớp trừu tượngThiết kế interface trong C++Ép kiểu động và RTTIÔn tập: Máy tính hình học3Hàm khởi tạo và hàm hủy
Hàm khởi tạo mặc địnhHàm khởi tạo có tham sốHàm khởi tạo sao chépHàm khởi tạo di chuyểnDanh sách khởi tạoHàm khởi tạo ủy quyềnTìm hiểu sâu về hàm hủyQuy tắc 3 / 5 / 0Ôn tập: lớp chuỗi6Nạ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ử số họcNạp chồng toán tử so sánhToán tử luồngNạp chồng toán tử gánNạp chồng toán tử [] và ()Toán tử chuyển đổi kiểuÔn tập: lớp ma trận9Mẫu
Mẫu hàmMẫu lớpChuyên biệt hóa mẫuMẫu biến tham sốCơ bản về SFINAE và đặc điểm kiểuÔn tập: vùng chứa tổng quátTự luyện tập: Trình biên dịch C++ online