Xử lý ngoại lệ (Exception Handling)
Ngoại lệ (exception) là cơ chế để xử lý các tình huống lỗi xảy ra lúc chạy chương trình (runtime errors) một cách có kiểm soát, thay vì để chương trình crash đột ngột.
try/catch cơ bản
Phần tiêu đề “try/catch cơ bản”#include <iostream>
int main() { try { int a = 10; int b = 0;
if (b == 0) { throw std::runtime_error("Khong the chia cho 0"); }
std::cout << a / b << std::endl; } catch (const std::runtime_error& e) { std::cout << "Loi: " << e.what() << std::endl; }
std::cout << "Chuong trinh tiep tuc chay binh thuong" << std::endl;
return 0;}try { ... }: khối code có khả năng gây ra lỗithrow: “ném” ra một ngoại lệ khi phát hiện tình huống lỗicatch (kiểu_exception& e) { ... }: “bắt” và xử lý ngoại lệ tương ứng
Các kiểu exception chuẩn thường gặp
Phần tiêu đề “Các kiểu exception chuẩn thường gặp”Thư viện chuẩn (<stdexcept>) cung cấp sẵn nhiều kiểu exception cho các tình huống phổ biến:
#include <stdexcept>#include <iostream>#include <vector>
int main() { try { std::vector<int> numbers = {1, 2, 3}; std::cout << numbers.at(10) << std::endl; // Ném std::out_of_range } catch (const std::out_of_range& e) { std::cout << "Loi pham vi: " << e.what() << std::endl; }
try { throw std::invalid_argument("Gia tri khong hop le"); } catch (const std::invalid_argument& e) { std::cout << "Loi doi so: " << e.what() << std::endl; }
return 0;}| Kiểu exception | Dùng khi nào |
|---|---|
std::runtime_error |
Lỗi chung xảy ra lúc chạy chương trình |
std::out_of_range |
Truy cập ngoài phạm vi hợp lệ (ví dụ vector::at) |
std::invalid_argument |
Đối số truyền vào không hợp lệ |
std::logic_error |
Lỗi logic trong chương trình (có thể phát hiện trước khi chạy) |
Bắt nhiều loại exception
Phần tiêu đề “Bắt nhiều loại exception”#include <iostream>#include <stdexcept>
void process(int value) { if (value < 0) { throw std::invalid_argument("Gia tri khong duoc am"); } if (value > 100) { throw std::out_of_range("Gia tri vuot qua 100"); } std::cout << "Xu ly gia tri: " << value << std::endl;}
int main() { try { process(-5); } catch (const std::invalid_argument& e) { std::cout << "Loi doi so: " << e.what() << std::endl; } catch (const std::out_of_range& e) { std::cout << "Loi pham vi: " << e.what() << std::endl; } catch (const std::exception& e) { // Bắt mọi exception khác kế thừa từ std::exception std::cout << "Loi khac: " << e.what() << std::endl; }
return 0;}catch (...): bắt mọi loại lỗi
Phần tiêu đề “catch (...): bắt mọi loại lỗi”try { // code có thể ném bất kỳ loại exception nào} catch (...) { std::cout << "Da xay ra loi khong xac dinh" << std::endl;}Nên hạn chế dùng catch (...) trừ khi thực sự cần thiết, vì nó “nuốt” mọi lỗi mà không cho biết chi tiết nguyên nhân.
Tự định nghĩa exception riêng
Phần tiêu đề “Tự định nghĩa exception riêng”Bạn có thể tạo kiểu exception riêng bằng cách kế thừa từ std::exception:
#include <exception>#include <iostream>
class InsufficientBalanceException : public std::exception {public: const char* what() const noexcept override { return "So du khong du de thuc hien giao dich"; }};
void withdraw(double balance, double amount) { if (amount > balance) { throw InsufficientBalanceException(); } std::cout << "Rut tien thanh cong: " << amount << std::endl;}
int main() { try { withdraw(100, 200); } catch (const InsufficientBalanceException& e) { std::cout << "Loi: " << e.what() << std::endl; }
return 0;}finally trong C++? Dùng RAII thay thế
Phần tiêu đề “finally trong C++? Dùng RAII thay thế”C++ không có khối finally như một số ngôn ngữ khác. Thay vào đó, nguyên lý RAII (đã đề cập ở bài Smart Pointers) đảm bảo tài nguyên luôn được giải phóng đúng cách khi một đối tượng ra khỏi phạm vi, dù có exception xảy ra hay không:
#include <memory>#include <iostream>
void process() { auto ptr = std::make_unique<int>(42); // RAII: tự động giải phóng dù có exception hay không throw std::runtime_error("Loi xay ra!"); // Bộ nhớ vẫn được giải phóng đúng cách nhờ unique_ptr, dù dòng dưới không bao giờ chạy tới}
int main() { try { process(); } catch (const std::exception& e) { std::cout << "Da bat loi: " << e.what() << std::endl; } return 0;}Tổng kết
Phần tiêu đề “Tổng kết”try/catch/throwcho phép xử lý lỗi lúc chạy chương trình một cách có kiểm soát- Thư viện chuẩn cung cấp sẵn nhiều kiểu exception (
runtime_error,out_of_range,invalid_argument…) - Có thể tự định nghĩa exception riêng bằng cách kế thừa từ
std::exception - C++ không có
finally- dùng RAII (smart pointer, destructor) để đảm bảo tài nguyên luôn được giải phóng đúng cách