Chuyển tới nội dung chính

Tổng kết các khuyến nghị và lỗi thường gặp khi xử lý ngoại lệ trong C++

Xử lý ngoại lệ (exception handling) là công cụ mạnh trong C++, nhưng nếu lạm dụng hoặc sử dụng sai cách sẽ gây lỗi khó dò và mất ổn định chương trình. Phần này tổng hợp các thực hành tốt (best practices)những lỗi phổ biến cần tránh, giúp bạn viết code an toàn và dễ bảo trì hơn.

Các thực hành tốt nên áp dụng

  1. Chỉ dùng ngoại lệ cho trường hợp lỗi bất thường

    • Ví dụ: Mở file thất bại, kết nối mạng bị ngắt, phân bổ bộ nhớ lỗi,...
    • Không nên dùng để kiểm soát luồng thông thường (if/else vẫn tốt hơn).
  2. Luôn catch theo kiểu tham chiếu (reference)

    try { ... }
    catch (const std::exception& e) {
    std::cerr << e.what();
    }
    • Giúp tránh sao chép đối tượng, giữ đúng kiểu ngoại lệ.
  3. Sử dụng các lớp ngoại lệ chuẩn (std::runtime_error, std::invalid_argument,...)

    • Có thông điệp rõ ràng và dễ tích hợp với thư viện khác.
  4. Dùng RAII để tự động quản lý tài nguyên

    • Tránh rò rỉ tài nguyên khi ngoại lệ xảy ra:
      std::unique_ptr<Resource> res(new Resource());
  5. Đảm bảo các destructor không ném ngoại lệ

    • Vì ngoại lệ trong khi phá hủy sẽ gây std::terminate() nếu đang trong quá trình unwinding.
  6. Dùng noexcept để đánh dấu hàm không ném ngoại lệ nếu chắc chắn

    • Giúp tối ưu hiệu năng và phát hiện lỗi khi biên dịch.

Các lỗi thường gặp nên tránh

  1. catch (...) mà không xử lý gì

    try { ... }
    catch (...) {} // ❌ không nên
    • Làm mất dấu lỗi, rất nguy hiểm. Nếu dùng catch(...), hãy log hoặc xử lý rõ ràng.
  2. Ném ngoại lệ từ destructor

    ~MyClass() {
    throw std::runtime_error("Lỗi!"); // ❌ tuyệt đối tránh
    }
    • Có thể gây chương trình crash vì C++ không cho phép ném ngoại lệ trong khi đang huỷ bỏ một ngoại lệ khác.
  3. Bắt ngoại lệ bằng giá trị (by value)

    catch (std::exception e) // ❌
    • Gây sao chép, mất thông tin kiểu gốc. Luôn dùng tham chiếu (const&).
  4. Không cung cấp thông điệp lỗi rõ ràng

    throw std::runtime_error("Lỗi"); // Không rõ lỗi gì
    throw std::runtime_error("Không mở được file cấu hình"); // Cung cấp thông điệp lỗi rõ ràng
  5. Lạm dụng try/catch ở mọi nơi

    • Chỉ nên đặt try/catch ở mức cao (main, thread, hoặc module), không nên chèn dày đặc trong hàm cấp thấp.

Mẹo viết code kiểm lỗi rõ ràng, an toàn

  • Dùng if để kiểm tra điều kiện đơn giản trước khi đến bước có thể ném ngoại lệ
  • Gói nhiều thao tác nguy hiểm vào một try duy nhất
  • Ghi log hoặc báo lỗi rõ ràng khi bắt được ngoại lệ
  • Tự viết hàm xử lý lỗi thống nhất để tái sử dụng
  • Ví dụ:
    void handleException(const std::exception& e) {
    std::cerr << "Lỗi: " << e.what() << '\n';
    }

    try {
    // code nguy hiểm
    } catch (const std::exception& e) {
    handleException(e);
    }
thông tin

Áp dụng các nguyên tắc này sẽ giúp bạn viết code an toàn, dễ debug và dễ bảo trì hơn trong môi trường C++ phức tạp.