Catch trong C++: 'Bắt' Lỗi Như Bắt Trend!
C++

Catch trong C++: 'Bắt' Lỗi Như Bắt Trend!

Author

Admin System

@root

Ngày xuất bản

19 Mar, 2026

Lượt xem

1 Lượt

"catch"

Chào các "coder hệ Gen Z"! Anh Creyt đây, hôm nay chúng ta sẽ cùng "flex" kiến thức về một khái niệm cực kỳ quan trọng trong C++: catch. Nghe có vẻ đơn giản, nhưng để dùng nó "chuẩn bài" thì không phải ai cũng biết đâu nhé!

1. catch là gì và để làm gì? (Kế hoạch B cho code)

Trong đời lập trình, đâu phải lúc nào code của chúng ta cũng chạy "mượt như nhung", "happy path" từ đầu đến cuối. Đôi khi, mọi thứ "toang" không báo trước: file không tồn tại, mạng rớt, người dùng nhập liệu "trời ơi đất hỡi", hay thậm chí là bộ nhớ đầy. Những lúc như vậy, nếu không có "kế hoạch B", ứng dụng của bạn sẽ "bay màu" ngay lập tức, người dùng thì "sốc ngang", còn bạn thì "đổ mồ hôi hột" tìm bug.

Đó chính là lúc try-catch "lên tiếng". Hãy hình dung thế này:

  • try: Là "sân khấu" nơi bạn cho code của mình "biểu diễn". Bạn tin là nó ổn, nhưng cũng hơi "rén" vì biết đâu có "phốt".
  • throw: Nếu có "phốt" (lỗi, ngoại lệ) xảy ra trong khối try, code sẽ "ném" ra một "tín hiệu SOS" - đó là một đối tượng ngoại lệ (exception).
  • catch: Và đây, catch chính là "cái lưới" siêu to khổng lồ mà bạn giăng ra để "tóm gọn" cái "tín hiệu SOS" đó. Nó "bắt" lấy ngoại lệ được throw ra, cho phép bạn xử lý tình huống khẩn cấp một cách "thanh lịch" mà không làm sập cả "sân khấu" (chương trình).

Nói cách khác, catch giúp chương trình của bạn "sống sót" qua những tình huống bất ngờ, giúp bạn kiểm soát lỗi, ghi log lại để "điều tra" sau này, hoặc đơn giản là hiển thị một thông báo "dễ thương" cho người dùng thay vì một màn hình đen "đáng sợ".

2. Code Ví Dụ Minh Họa: 'Bắt' lỗi như pro

Để các bạn dễ hình dung, chúng ta hãy xem một ví dụ kinh điển: chia cho số 0.

#include <iostream>
#include <string>
#include <stdexcept> // Để dùng các exception chuẩn như std::runtime_error

// Định nghĩa một loại exception tùy chỉnh của riêng chúng ta
class DivideByZeroException : public std::runtime_error {
public:
    DivideByZeroException(const std::string& msg) 
        : std::runtime_error("Lỗi chia cho 0: " + msg) {}
};

double divide(double numerator, double denominator) {
    if (denominator == 0) {
        // Nếu có lỗi, 'ném' ra một exception tùy chỉnh
        throw DivideByZeroException("Mẫu số không được bằng 0!");
    }
    return numerator / denominator;
}

int main() {
    double num1 = 10.0;
    double num2 = 0.0;
    double num3 = 2.0;

    // Kịch bản 1: Chia cho 0 - sẽ bị 'bắt'
    try {
        std::cout << "Kết quả của " << num1 << " / " << num2 << " là: ";
        double result = divide(num1, num2);
        std::cout << result << std::endl; // Dòng này sẽ không được thực thi
    } catch (const DivideByZeroException& e) { // Bắt exception tùy chỉnh của chúng ta
        std::cerr << "*** Lỗi: " << e.what() << " ***" << std::endl;
    } catch (const std::exception& e) { // Bắt các exception chuẩn khác
        std::cerr << "*** Lỗi chung: " << e.what() << " ***" << std::endl;
    } catch (...) { // Bắt tất cả các loại exception còn lại (catch-all)
        std::cerr << "*** Lỗi không xác định đã xảy ra! ***" << std::endl;
    }

    std::cout << "\n--------------------------------\n\n";

    // Kịch bản 2: Chia bình thường - sẽ không bị 'bắt'
    try {
        std::cout << "Kết quả của " << num1 << " / " << num3 << " là: ";
        double result = divide(num1, num3);
        std::cout << result << std::endl;
    } catch (const DivideByZeroException& e) {
        std::cerr << "*** Lỗi: " << e.what() << " ***" << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "*** Lỗi chung: " << e.what() << " ***" << std::endl;
    } catch (...) {
        std::cerr << "*** Lỗi không xác định đã xảy ra! ***" << std::endl;
    }

    return 0;
}

Trong ví dụ trên:

  • Chúng ta định nghĩa một DivideByZeroException kế thừa từ std::runtime_error để tạo ra một loại lỗi riêng biệt.
  • Hàm divide sẽ throw exception này nếu mẫu số bằng 0.
  • Khối try bao quanh lời gọi hàm divide.
  • Các khối catch được sắp xếp từ cụ thể đến tổng quát: DivideByZeroException (của ta), rồi đến std::exception (của C++), và cuối cùng là ... (bắt tất cả).
Illustration

3. Mẹo (Best Practices) để 'bắt' lỗi chuẩn khỏi chỉnh

Để trở thành một "thợ săn lỗi" chuyên nghiệp, hãy bỏ túi vài mẹo sau:

  • "Bắt" đúng loại: Luôn ưu tiên catch các exception cụ thể trước. Ví dụ, catch (const DivideByZeroException& e) sẽ được xử lý trước catch (const std::exception& e). Việc này giống như bạn có bộ lọc thông minh, chỉ bắt những loại cá bạn muốn thôi.
  • "Bắt" bằng tham chiếu const&: Thay vì catch (std::exception e) (bắt bằng giá trị, tạo bản sao), hãy dùng catch (const std::exception& e). Nó hiệu quả hơn nhiều, tránh được tình trạng "object slicing" (mất thông tin của exception con khi bắt bằng exception cha) và cho phép đa hình.
  • Đừng "nuốt chửng" lỗi: Đừng bao giờ catch một exception rồi để trống khối catch hoặc chỉ in ra một câu "Lỗi rồi!" chung chung. Hãy luôn ghi log chi tiết, hoặc ít nhất là thông báo cho người dùng một cách rõ ràng. Lỗi mà bị "nuốt" đi, sau này debug bạn sẽ "đổ mồ hôi hột" tìm nó đấy.
  • RAII là "chân ái" cho tài nguyên: try-catch tuyệt vời cho việc xử lý ngoại lệ. Nhưng để đảm bảo tài nguyên (file, bộ nhớ, khóa...) được giải phóng dù có lỗi hay không, hãy dùng RAII (Resource Acquisition Is Initialization). Ví dụ như std::unique_ptr cho bộ nhớ, std::lock_guard cho mutex. Chúng tự động dọn dẹp khi ra khỏi scope, "đỉnh của chóp"!
  • Chỉ dùng cho trường hợp "bất thường": Exception handling có chi phí hiệu năng. Đừng dùng nó để kiểm tra các điều kiện "bình thường" như kiểm tra người dùng nhập số âm hay một vector rỗng. Những trường hợp đó, if-else là "đúng bài" hơn nhiều.

4. Ứng dụng thực tế: catch ở khắp mọi nơi

catch không chỉ là lý thuyết, nó được ứng dụng "ngập tràn" trong các hệ thống "khủng" mà bạn dùng hàng ngày:

  • Trình duyệt web (ví dụ Google Chrome): Khi một tab bị lỗi nặng (ví dụ, một script JavaScript chạy sai gây tràn bộ nhớ), trình duyệt sẽ catch lỗi đó và chỉ làm sập tab đó thôi, không ảnh hưởng đến các tab khác hay toàn bộ trình duyệt. Bạn sẽ thấy thông báo "Trang này đã gặp sự cố".
  • Ứng dụng di động (ví dụ Spotify, Facebook): Khi bạn mất kết nối mạng, ứng dụng sẽ không "crash" mà sẽ catch lỗi mạng, hiển thị thông báo "Không có kết nối Internet" và cho phép bạn thử lại.
  • Hệ thống quản lý cơ sở dữ liệu: Khi kết nối đến database thất bại, hoặc một truy vấn SQL bị lỗi cú pháp, hệ thống sẽ throw exception và ứng dụng của bạn sẽ catch để xử lý, ví dụ như hiển thị thông báo lỗi cho người dùng hoặc thử kết nối lại.
  • Game engines: Khi một tài nguyên game (texture, model) không thể tải được do file bị hỏng hoặc không tồn tại, engine sẽ catch lỗi và có thể tải một tài nguyên mặc định hoặc hiển thị lỗi để nhà phát triển sửa.

5. Thử nghiệm của Creyt và lời khuyên

Hồi mới "nhập môn" C++, anh Creyt cũng từng "vật lộn" với try-catch. Ban đầu, anh hay có thói quen catch (...) (bắt tất cả) để chương trình không bị crash, nhưng rồi lại "đau đầu" vì không biết lỗi cụ thể là gì để mà sửa. Đó là một bài học đắt giá về việc phải "bắt" có chọn lọc.

Khi nào nên dùng try-catch?

  • Khi tương tác với các thư viện bên thứ ba: Bạn không kiểm soát được code của họ, nên hãy chuẩn bị "đón lỗi" từ họ.
  • Khi làm việc với tài nguyên bên ngoài: File I/O, network, database. Những thứ này luôn tiềm ẩn rủi ro.
  • Khi một lỗi là thực sự ngoại lệ: Nghĩa là nó không nên xảy ra trong luồng hoạt động bình thường của chương trình. Ví dụ, một file cấu hình quan trọng bị thiếu, chứ không phải việc người dùng nhập sai tuổi.

Khi nào không nên dùng try-catch?

  • Thay thế cho if-else: Đừng dùng exception để kiểm tra các điều kiện thông thường. if (file.exists()) { ... } else { ... } tốt hơn nhiều so với try { open_file(); } catch (FileNotFoundException) { ... }.
  • Trong các vòng lặp hiệu năng cao: Chi phí của exception handling (stack unwinding, tìm kiếm catch block phù hợp) có thể rất lớn và làm chậm chương trình của bạn.

catch là một công cụ mạnh mẽ, nhưng như mọi công cụ khác, nó cần được sử dụng đúng lúc, đúng chỗ. Hãy "bắt" lỗi một cách thông minh, và code của bạn sẽ "chất" hơn rất nhiều!

Thuộc Series: C++

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!