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

Cơ bản về ngoại lệ trong C++

Khái niệm cơ bản

  • Exception (ngoại lệ) là cơ chế trong C++ dùng để xử lý lỗi phát sinh trong quá trình chương trình thực thi.
  • Khi một lỗi xảy ra, chương trình có thể ném (throw) một ngoại lệ, sau đó tìm khối xử lý ngoại lệ (catch) phù hợp để xử lý lỗi đó.
  • Cơ chế exception giúp tách biệt logic xử lý lỗi khỏi logic xử lý chính của chương trình.

Từ khóa chính

  • try: dùng để bao một đoạn code có thể phát sinh lỗi.
  • throw: ném ra một ngoại lệ (thường là một giá trị hoặc đối tượng).
  • catch: dùng để bắt và xử lý ngoại lệ được ném từ khối try.

Cú pháp cơ bản

try {
// Mã có thể phát sinh lỗi
throw "Lỗi xảy ra!";
} catch (const char* msg) {
std::cerr << "Đã bắt ngoại lệ: " << msg << '\n';
}
  • Nếu dòng throw được thực thi, chương trình sẽ thoát khỏi try và chuyển đến catch đầu tiên có kiểu tương ứng.
  • Nếu không có catch phù hợp, chương trình sẽ kết thúc bằng std::terminate.

Thứ tự hoạt động:

  1. Chạy mã trong try.
  2. Nếu throw không xảy ra → bỏ qua catch.
  3. Nếu throw xảy ra → tìm catch phù hợp theo thứ tự từ trên xuống.
  4. Nếu tìm được → nhảy vào catch tương ứng.
  5. Nếu không tìm được → gọi std::terminate() và kết thúc chương trình.

Ví dụ chi tiết

#include <iostream>

int chia(int a, int b) {
if (b == 0)
throw std::runtime_error("Chia cho 0");
return a / b;
}

int main() {
try {
std::cout << chia(10, 2) << '\n'; // In ra 5
std::cout << chia(5, 0) << '\n'; // Gây ngoại lệ
} catch (const std::runtime_error& e) {
std::cerr << "Lỗi: " << e.what() << '\n';
}
}
  • Hàm chia ném ngoại lệ std::runtime_error nếu chia cho 0.
  • Khối catch bắt được lỗi và in thông báo rõ ràng.

Các kiểu giá trị có thể throw

  • Kiểu nguyên thủy: throw 42;
  • Chuỗi ký tự: throw "Lỗi";
  • Đối tượng: throw std::runtime_error("Lỗi nghiêm trọng");
  • Bất kỳ kiểu nào, nhưng nên dùng đối tượng kế thừa từ std::exception để thuận tiện cho việc xử lý và gỡ lỗi.

Ghi chú quan trọng

  • Không nên throw kiểu nguyên thủy (int, const char*) trong dự án thực tế. Hãy luôn throw các đối tượng cụ thể kế thừa từ std::exception.
  • throw có thể dùng trong hàm, constructor, hoặc bất kỳ đoạn code nào.
  • C++ không hỗ trợ finally như Java, nhưng bạn có thể dùng RAII hoặc std::unique_ptr, std::lock_guard,... để thay thế finally.

Exception propagation (truyền ngoại lệ)

  • Nếu throw trong một hàm không được bắt tại chỗ, ngoại lệ sẽ "lan truyền" lên caller (nơi gọi) cho đến khi gặp try phù hợp.

Ví dụ

void test() {
throw std::runtime_error("Lỗi test()");
}

int main() {
try {
test(); // Ngoại lệ từ test() được bắt ở đây
} catch (const std::exception& e) {
std::cerr << "Caught: " << e.what() << '\n';
}
}