Bài 12: Nạp chồng hàm và Tham số mặc định (Function Overloading & Default Parameters)
📖 Giới thiệu
Function Overloading (nạp chồng hàm) và Default Parameters (tham số mặc định) là hai tính năng mạnh mẽ của C++ giúp tăng tính linh hoạt và khả năng sử dụng của code. Chúng cho phép:
- Sử dụng cùng tên hàm cho các thao tác tương tự với kiểu dữ liệu khác nhau
- Giảm số lượng tham số cần truyền vào khi gọi hàm
- Tăng tính trực quan và dễ nhớ của API
- Tương thích ngược khi mở rộng chức năng
Các ứng dụng quan trọng:
- Thư viện toán học:
max(int, int),max(double, double),max(string, string) - Xử lý dữ liệu:
print(int),print(string),print(vector) - Giao diện người dùng:
connect(server),connect(server, port),connect(server, port, timeout)
🔧 Cú pháp
Function Overloading
cpp
// Các hàm cùng tên, khác signature
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
string add(const string& a, const string& b) {
return a + b;
}
// Khác nhau về số lượng tham số
int multiply(int a, int b) {
return a * b;
}
int multiply(int a, int b, int c) {
return a * b * c;
}
// Khác nhau về kiểu tham số
void process(int value) {
cout << "Processing integer: " << value << endl;
}
void process(const string& value) {
cout << "Processing string: " << value << endl;
}
void process(const vector<int>& values) {
cout << "Processing vector with " << values.size() << " elements" << endl;
}Default Parameters
cpp
// Tham số mặc định ở cuối
void greet(const string& name, const string& greeting = "Hello", bool formal = false) {
if (formal) {
cout << greeting << ", Mr./Ms. " << name << endl;
} else {
cout << greeting << ", " << name << "!" << endl;
}
}
// Gọi hàm với các cách khác nhau
greet("An"); // "Hello, An!"
greet("An", "Hi"); // "Hi, An!"
greet("An", "Good morning", true); // "Good morning, Mr./Ms. An"
// Hàm với nhiều tham số mặc định
void connectToServer(const string& server,
int port = 80,
int timeout = 30,
bool useSSL = false) {
cout << "Connecting to " << server << ":" << port;
if (useSSL) cout << " (SSL)";
cout << " with timeout " << timeout << "s" << endl;
}Quy tắc Overloading
cpp
// ✅ Hợp lệ: Khác kiểu tham số
void func(int x) { }
void func(double x) { }
// ✅ Hợp lệ: Khác số lượng tham số
void func(int x) { }
void func(int x, int y) { }
// ✅ Hợp lệ: Khác thứ tự tham số
void func(int x, double y) { }
void func(double x, int y) { }
// ❌ Không hợp lệ: Chỉ khác return type
int func(int x) { return x; }
void func(int x) { } // Lỗi!
// ❌ Không hợp lệ: Chỉ khác tên tham số
void func(int x) { }
void func(int y) { } // Lỗi!🔬 Phân tích & Giải thích chi tiết
Function Overloading Resolution
Khi gọi hàm overloaded, compiler sẽ chọn hàm phù hợp nhất theo thứ tự:
- Exact Match: Khớp chính xác kiểu dữ liệu
- Promotion: Chuyển đổi kiểu tự động (int → double)
- Standard Conversion: Chuyển đổi chuẩn
- User-defined Conversion: Chuyển đổi do người dùng định nghĩa
cpp
void func(int x) { cout << "int version" << endl; }
void func(double x) { cout << "double version" << endl; }
int main() {
func(5); // Gọi func(int) - exact match
func(5.0); // Gọi func(double) - exact match
func(5.5f); // Gọi func(double) - promotion float → double
char c = 'A';
func(c); // Gọi func(int) - promotion char → int
}Default Parameters Best Practices
cpp
// ✅ Tốt: Tham số mặc định ở cuối
void goodFunction(int required, int optional1 = 10, int optional2 = 20);
// ❌ Tránh: Tham số mặc định ở giữa
void badFunction(int optional1 = 10, int required, int optional2 = 20);
// ✅ Tốt: Sử dụng giá trị có ý nghĩa
void setColor(int red, int green = 0, int blue = 0, int alpha = 255);
// ✅ Tốt: Documentation cho default values
void processFile(const string& filename,
bool backup = true, // Tự động backup
int bufferSize = 4096, // 4KB buffer
bool overwrite = false); // Không ghi đèKhi nào sử dụng
Function Overloading:
- Cùng chức năng, khác kiểu dữ liệu
- APIs cần tính nhất quán
- Mathematical operations
- Type-safe interfaces
Default Parameters:
- Có giá trị hợp lý mặc định
- Backward compatibility
- Configuration functions
- Optional features
💻 Ví dụ minh họa
Ví dụ 1: Thư viện xử lý hình học
cpp
#include <iostream>
#include <vector>
#include <cmath>
#include <iomanip>
using namespace std;
// Overloaded functions cho diện tích
double area(double radius) {
return M_PI * radius * radius;
}
double area(double length, double width) {
return length * width;
}
double area(double a, double b, double c) {
// Công thức Heron cho tam giác
double s = (a + b + c) / 2;
return sqrt(s * (s - a) * (s - b) * (s - c));
}
// Overloaded functions cho chu vi
double perimeter(double radius) {
return 2 * M_PI * radius;
}
double perimeter(double length, double width) {
return 2 * (length + width);
}
double perimeter(double a, double b, double c) {
return a + b + c;
}
// Function với default parameters
void printShapeInfo(const string& shapeName,
double area,
double perimeter,
int precision = 2,
bool showFormula = false,
const string& unit = "cm") {
cout << fixed << setprecision(precision);
cout << "\n=== THONG TIN " << shapeName << " ===" << endl;
cout << "Dien tich: " << area << " " << unit << "²" << endl;
cout << "Chu vi: " << perimeter << " " << unit << endl;
if (showFormula) {
if (shapeName == "HINH TRON") {
cout << "Cong thuc: S = πr², P = 2πr" << endl;
} else if (shapeName == "HINH CHU NHAT") {
cout << "Cong thuc: S = d×r, P = 2(d+r)" << endl;
} else if (shapeName == "TAM GIAC") {
cout << "Cong thuc: S = √[s(s-a)(s-b)(s-c)], P = a+b+c" << endl;
}
}
}
// Overloaded validation functions
bool isValid(double radius) {
return radius > 0;
}
bool isValid(double length, double width) {
return length > 0 && width > 0;
}
bool isValid(double a, double b, double c) {
return a > 0 && b > 0 && c > 0 &&
a + b > c && b + c > a && c + a > b;
}
// Main calculation functions
void calculateCircle() {
double radius;
cout << "Nhap ban kinh hinh tron: ";
cin >> radius;
if (!isValid(radius)) {
cout << "Ban kinh phai duong!" << endl;
return;
}
double circleArea = area(radius);
double circlePerimeter = perimeter(radius);
printShapeInfo("HINH TRON", circleArea, circlePerimeter, 3, true, "cm");
}
void calculateRectangle() {
double length, width;
cout << "Nhap chieu dai: ";
cin >> length;
cout << "Nhap chieu rong: ";
cin >> width;
if (!isValid(length, width)) {
cout << "Chieu dai va chieu rong phai duong!" << endl;
return;
}
double rectArea = area(length, width);
double rectPerimeter = perimeter(length, width);
printShapeInfo("HINH CHU NHAT", rectArea, rectPerimeter, 2, true, "m");
}
void calculateTriangle() {
double a, b, c;
cout << "Nhap 3 canh tam giac: ";
cin >> a >> b >> c;
if (!isValid(a, b, c)) {
cout << "3 canh khong hop le (phai duong va thoa man bat dang thuc tam giac)!" << endl;
return;
}
double triArea = area(a, b, c);
double triPerimeter = perimeter(a, b, c);
printShapeInfo("TAM GIAC", triArea, triPerimeter, 2, true, "cm");
}
// Menu system with default parameters
void showMenu(bool detailed = false, const string& title = "TINH TOAN HINH HOC") {
cout << "\n========== " << title << " ==========" << endl;
cout << "1. Hinh tron" << endl;
cout << "2. Hinh chu nhat" << endl;
cout << "3. Tam giac" << endl;
cout << "0. Thoat" << endl;
if (detailed) {
cout << "\nMoi hinh se tinh dien tich va chu vi" << endl;
cout << "Cac cong thuc se duoc hien thi" << endl;
}
cout << "Lua chon: ";
}
int main() {
int choice;
bool showDetailedMenu = false;
cout << "Ban co muon hien thi menu chi tiet? (1: Co, 0: Khong): ";
cin >> showDetailedMenu;
do {
showMenu(showDetailedMenu);
cin >> choice;
switch (choice) {
case 1:
calculateCircle();
break;
case 2:
calculateRectangle();
break;
case 3:
calculateTriangle();
break;
case 0:
cout << "Cam on ban da su dung chuong trinh!" << endl;
break;
default:
cout << "Lua chon khong hop le!" << endl;
}
} while (choice != 0);
return 0;
}Ví dụ 2: Hệ thống đăng nhập với nhiều phương thức
cpp
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <ctime>
using namespace std;
struct User {
string username;
string password;
string email;
string fullName;
time_t lastLogin;
int loginAttempts;
bool isLocked;
};
class LoginSystem {
private:
map<string, User> users;
string currentUser;
public:
// Overloaded register functions
bool registerUser(const string& username, const string& password) {
return registerUser(username, password, "", "", false);
}
bool registerUser(const string& username,
const string& password,
const string& email) {
return registerUser(username, password, email, "", false);
}
bool registerUser(const string& username,
const string& password,
const string& email,
const string& fullName,
bool sendWelcomeEmail = true) {
if (users.find(username) != users.end()) {
cout << "Ten dang nhap da ton tai!" << endl;
return false;
}
User newUser;
newUser.username = username;
newUser.password = password;
newUser.email = email;
newUser.fullName = fullName.empty() ? username : fullName;
newUser.lastLogin = 0;
newUser.loginAttempts = 0;
newUser.isLocked = false;
users[username] = newUser;
cout << "Dang ky thanh cong cho: " << newUser.fullName << endl;
if (sendWelcomeEmail && !email.empty()) {
cout << "Email chao mung da duoc gui toi: " << email << endl;
}
return true;
}
// Overloaded login functions
bool login(const string& username, const string& password) {
return login(username, password, true, 30);
}
bool login(const string& username,
const string& password,
bool rememberMe) {
return login(username, password, rememberMe, 30);
}
bool login(const string& username,
const string& password,
bool rememberMe = true,
int sessionTimeout = 30) {
auto it = users.find(username);
if (it == users.end()) {
cout << "Ten dang nhap khong ton tai!" << endl;
return false;
}
User& user = it->second;
if (user.isLocked) {
cout << "Tai khoan da bi khoa!" << endl;
return false;
}
if (user.password != password) {
user.loginAttempts++;
cout << "Mat khau sai! Lan thu " << user.loginAttempts << "/3" << endl;
if (user.loginAttempts >= 3) {
user.isLocked = true;
cout << "Tai khoan da bi khoa do nhap sai qua nhieu lan!" << endl;
}
return false;
}
// Đăng nhập thành công
user.loginAttempts = 0;
user.lastLogin = time(0);
currentUser = username;
cout << "Dang nhap thanh cong!" << endl;
cout << "Chao mung, " << user.fullName << "!" << endl;
if (rememberMe) {
cout << "Phien lam viec se duoc luu trong " << sessionTimeout << " phut" << endl;
}
return true;
}
// Display functions with default parameters
void displayUserInfo(const string& username = "",
bool showSensitive = false,
bool showStats = true) {
string targetUser = username.empty() ? currentUser : username;
if (targetUser.empty()) {
cout << "Chua dang nhap!" << endl;
return;
}
auto it = users.find(targetUser);
if (it == users.end()) {
cout << "Khong tim thay nguoi dung!" << endl;
return;
}
const User& user = it->second;
cout << "\n=== THONG TIN NGUOI DUNG ===" << endl;
cout << "Ten dang nhap: " << user.username << endl;
cout << "Ten day du: " << user.fullName << endl;
cout << "Email: " << user.email << endl;
if (showSensitive && targetUser == currentUser) {
cout << "Mat khau: " << string(user.password.length(), '*') << endl;
}
if (showStats) {
cout << "Lan dang nhap cuoi: ";
if (user.lastLogin > 0) {
cout << ctime(&user.lastLogin);
} else {
cout << "Chua bao gio" << endl;
}
cout << "So lan dang nhap sai: " << user.loginAttempts << endl;
cout << "Trang thai: " << (user.isLocked ? "Bi khoa" : "Hoat dong") << endl;
}
}
void logout(bool showMessage = true) {
if (showMessage && !currentUser.empty()) {
cout << "Tam biet, " << users[currentUser].fullName << "!" << endl;
}
currentUser = "";
}
void listUsers(bool showDetails = false,
bool includeStats = false) {
cout << "\n=== DANH SACH NGUOI DUNG ===" << endl;
for (const auto& pair : users) {
const User& user = pair.second;
cout << "- " << user.username;
if (showDetails) {
cout << " (" << user.fullName << ")";
if (!user.email.empty()) {
cout << " <" << user.email << ">";
}
}
if (includeStats) {
cout << " [" << (user.isLocked ? "KHOA" : "HOAT_DONG") << "]";
}
cout << endl;
}
cout << "Tong so: " << users.size() << " nguoi dung" << endl;
}
bool isLoggedIn() const {
return !currentUser.empty();
}
string getCurrentUser() const {
return currentUser;
}
};
int main() {
LoginSystem system;
int choice;
// Tạo sẵn một số tài khoản mẫu
system.registerUser("admin", "admin123", "admin@example.com", "Administrator", false);
system.registerUser("user1", "password1", "user1@example.com", "Nguyen Van A");
system.registerUser("test", "test123");
do {
cout << "\n=== HE THONG DANG NHAP ===" << endl;
if (system.isLoggedIn()) {
cout << "Dang dang nhap voi: " << system.getCurrentUser() << endl;
}
cout << "1. Dang ky tai khoan" << endl;
cout << "2. Dang nhap" << endl;
cout << "3. Xem thong tin nguoi dung" << endl;
cout << "4. Danh sach nguoi dung" << endl;
cout << "5. Dang xuat" << endl;
cout << "0. Thoat" << endl;
cout << "Lua chon: ";
cin >> choice;
cin.ignore();
switch (choice) {
case 1: {
string username, password, email, fullName;
cout << "Ten dang nhap: ";
getline(cin, username);
cout << "Mat khau: ";
getline(cin, password);
cout << "Email (tuy chon): ";
getline(cin, email);
cout << "Ten day du (tuy chon): ";
getline(cin, fullName);
if (email.empty() && fullName.empty()) {
system.registerUser(username, password);
} else if (fullName.empty()) {
system.registerUser(username, password, email);
} else {
system.registerUser(username, password, email, fullName);
}
break;
}
case 2: {
string username, password;
bool rememberMe;
cout << "Ten dang nhap: ";
getline(cin, username);
cout << "Mat khau: ";
getline(cin, password);
cout << "Ghi nho dang nhap? (1/0): ";
cin >> rememberMe;
system.login(username, password, rememberMe);
break;
}
case 3: {
if (system.isLoggedIn()) {
bool showSensitive, showStats;
cout << "Hien thi thong tin mat khau? (1/0): ";
cin >> showSensitive;
cout << "Hien thi thong ke? (1/0): ";
cin >> showStats;
system.displayUserInfo("", showSensitive, showStats);
} else {
cout << "Vui long dang nhap truoc!" << endl;
}
break;
}
case 4: {
bool showDetails, includeStats;
cout << "Hien thi chi tiet? (1/0): ";
cin >> showDetails;
cout << "Hien thi thong ke? (1/0): ";
cin >> includeStats;
system.listUsers(showDetails, includeStats);
break;
}
case 5:
system.logout();
break;
case 0:
cout << "Tam biet!" << endl;
break;
default:
cout << "Lua chon khong hop le!" << endl;
}
} while (choice != 0);
return 0;
}🏋️ Thực hành
Bài tập 1: Overloaded Math Functions
cpp
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
// Overloaded max functions
int max(int a, int b) {
return (a > b) ? a : b;
}
double max(double a, double b) {
return (a > b) ? a : b;
}
int max(const vector<int>& numbers) {
if (numbers.empty()) return 0;
int maxVal = numbers[0];
for (int num : numbers) {
if (num > maxVal) maxVal = num;
}
return maxVal;
}
// Overloaded average functions
double average(int a, int b) {
return (a + b) / 2.0;
}
double average(double a, double b) {
return (a + b) / 2.0;
}
double average(const vector<int>& numbers) {
if (numbers.empty()) return 0.0;
int sum = 0;
for (int num : numbers) {
sum += num;
}
return (double)sum / numbers.size();
}
double average(const vector<double>& numbers) {
if (numbers.empty()) return 0.0;
double sum = 0.0;
for (double num : numbers) {
sum += num;
}
return sum / numbers.size();
}
int main() {
// Test overloaded functions
cout << "max(5, 3) = " << max(5, 3) << endl;
cout << "max(5.5, 3.3) = " << max(5.5, 3.3) << endl;
vector<int> intNumbers = {1, 5, 3, 9, 2};
cout << "max({1,5,3,9,2}) = " << max(intNumbers) << endl;
cout << "average(10, 20) = " << average(10, 20) << endl;
cout << "average(10.5, 20.5) = " << average(10.5, 20.5) << endl;
cout << "average({1,5,3,9,2}) = " << average(intNumbers) << endl;
return 0;
}Bài tập 2: Print Functions với Default Parameters
cpp
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
// Print với default formatting
void print(int value, int width = 10, char fill = ' ') {
cout << setw(width) << setfill(fill) << value << endl;
}
void print(double value, int width = 10, int precision = 2, char fill = ' ') {
cout << fixed << setprecision(precision)
<< setw(width) << setfill(fill) << value << endl;
}
void print(const string& text, int width = 20, bool leftAlign = true) {
if (leftAlign) {
cout << left << setw(width) << text << endl;
} else {
cout << right << setw(width) << text << endl;
}
cout << left; // Reset alignment
}
void print(const vector<int>& numbers,
const string& separator = ", ",
const string& prefix = "[",
const string& suffix = "]") {
cout << prefix;
for (int i = 0; i < numbers.size(); i++) {
cout << numbers[i];
if (i < numbers.size() - 1) {
cout << separator;
}
}
cout << suffix << endl;
}
int main() {
cout << "=== TEST PRINT FUNCTIONS ===" << endl;
// Integer printing
print(42); // Default width=10, fill=' '
print(42, 15); // Width=15, fill=' '
print(42, 15, '0'); // Width=15, fill='0'
// Double printing
print(3.14159); // Default width=10, precision=2
print(3.14159, 12); // Width=12, precision=2
print(3.14159, 12, 4); // Width=12, precision=4
// String printing
print("Hello"); // Default width=20, leftAlign=true
print("Hello", 30); // Width=30, leftAlign=true
print("Hello", 30, false); // Width=30, leftAlign=false
// Vector printing
vector<int> numbers = {1, 2, 3, 4, 5};
print(numbers); // Default: [1, 2, 3, 4, 5]
print(numbers, " | "); // Custom separator: [1 | 2 | 3 | 4 | 5]
print(numbers, "-", "{", "}"); // Custom all: {1-2-3-4-5}
return 0;
}📋 Tóm tắt
Các điểm quan trọng
Function Overloading:
- Cùng tên, khác signature (số/kiểu tham số)
- Compiler chọn hàm phù hợp tự động
- Tăng tính trực quan của API
Default Parameters:
- Phải đặt ở cuối danh sách tham số
- Cung cấp giá trị hợp lý mặc định
- Tăng tính linh hoạt khi gọi hàm
Best Practices:
- Sử dụng overloading cho cùng chức năng, khác kiểu
- Default parameters cho optional features
- Đặt tên rõ ràng, documentation đầy đủ
Lợi ích
- Code reuse: Tái sử dụng tên hàm cho logic tương tự
- Type safety: Compiler kiểm tra kiểu tự động
- Flexibility: Linh hoạt trong cách gọi hàm
- Maintainability: Dễ bảo trì và mở rộng
Chuẩn bị cho bài tiếp theo
Bài tiếp theo chúng ta sẽ học về Recursion (Đệ quy):
- Khái niệm và cơ chế hoạt động
- Base case và recursive case
- Ứng dụng: factorial, fibonacci, tree traversal
- Optimization techniques
Function overloading và default parameters là nền tảng để thiết kế APIs linh hoạt và dễ sử dụng!