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) và 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
-
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).
-
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ệ.
-
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.
-
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());
- Tránh rò rỉ tài nguyên khi ngoại lệ xảy ra:
-
Đả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.
- Vì ngoại lệ trong khi phá hủy sẽ gây
-
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
-
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.
- Làm mất dấu lỗi, rất nguy hiểm. Nếu dùng
-
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.
-
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&
).
- Gây sao chép, mất thông tin kiểu gốc. Luôn dùng tham chiếu (
-
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 -
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.
- Chỉ nên đặt
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);
}
Á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.