Skip to content

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ự:

  1. Exact Match: Khớp chính xác kiểu dữ liệu
  2. Promotion: Chuyển đổi kiểu tự động (int → double)
  3. Standard Conversion: Chuyển đổi chuẩn
  4. 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

  1. 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
  2. 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
  3. 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!

Khóa học C++ miễn phí