Trong suốt quá trình phát triển một
chương trình, có thể có một số trường
hợp mà một số đoạn mã chạy sai do truy
xuất đến những tài nguyên không tồn tại hay
vượt ra ngoài khoảng mong muốn...
Những loại tình huống bất
thường này được nằm trong cái
được gọi là exceptions và C++ đã vừa tích
hợp ba toán tử mới để xử lý những tình
huống này: try, throw và catch.
Dạng thức sử dụng như sau:
try { // đoạn mã cần thử
throw exception;
}
catch (type exception)
{ // đoạn được thực hiện trong trường hợp có lỗi
}
Nguyên tắc hoạt
động:
- Đoạn mã nằm trong khối try được thực hiện
một cách bình thường. Trong trường hợp có
lỗi xảy ra, đoạn mã này phải sử dụng
từ khoá throw và một tham
số để báo lỗi. Kiểu tham số này mô tả
chi tiết hoá lỗi và có thể là bất kì kiểu
hợp lệ nào.
- Nếu có lỗi xảy ra, nếu lệnh throw đã được
thực hiện bên trong khối try, khối catch sẽ được
thực hiện và nhận tham số được
truyền bởi throw.
Ví dụ:
// exceptions #include <iostream.h> int main () { char myarray[10]; try { for (int n=0; n<=10; n++) { if (n>9) throw "Out of range"; myarray[n]='z'; } } catch (char * str) { cout << "Exception: " << str << endl; } return 0; } |
Exception: Out of range
|
Trong ví dụ này, nếu bên trong vòng lặp
mà n lớn hơn 9 thì một lỗi sẽ
được thông báo vì myarray[n] trong trường hợp
đó có thể trỏ đến địa chỉ ô
nhớ không tin cậy. Khi throw được thực
hiện, khối try ngay lập
tức kết thúc và mọi đối tượng
được tạo bên trong khối try bị phá huỷ. Sau
đó, quyền điều khiển được
chuyển cho khối catch tương ứng (chỉ được
thực hiện trong những tình huống như thế
này). Cuối cùng chương trình tiếp tục ngay sau
khối, trong trường hợp này: return 0;.
Cú pháp được sử dụng bởi
throw tương
tự với return: Chỉ có một
tham số và không cần đặt nó nằm trong cặp
ngoặc đơn.
Khối catch phải nằm ngay sau
khối try mà không được
có đoạn mã nào nằm giữa chúng. Tham số mà catch chấp nhận có thể
là bất kì kiểu dữ liệu hợp lệ nào. Hơn
nữa, catch có thể
được quá tải để có thể chấp
nhận nhiều kiểu dữ liệu khác nhau. Trong
trường hợp này khối catch được thực
hiện là khối phù hợp với kiểu của tham
số được gửi đến bởi throw:
// exceptions: multiple catch blocks #include <iostream.h> int main () { try { char * mystring; mystring = new char [10]; if (mystring == NULL) throw "Allocation failure"; for (int n=0; n<=100; n++) { if (n>9) throw n; mystring[n]='z'; } } catch (int i) { cout << "Exception: "; cout << "index " << i << " is out of range" << endl; } catch (char * str) { cout << "Exception: " << str << endl; } return 0; } |
Exception: index 10 is out of range
|
Ở đây có thể có hai trường
hợp xảy ra:
1.
Khối dữ liệu 10 kí tự không
thể được cấp phát (gần như là
chẳng bao giờ xảy ra nhưng không có nghĩa là không
thể): lỗi này sẽ bị chặn bởi catch (to char * str).
2.
Chỉ số cực đại của mystring đã bị vượt
quá: lỗi này sẽ bị chặn bởi catch (int i), since parameter is an
integer number.
Chúng ta có thể định nghĩa một
khối catch để
chặn tất cả các exceptions mà không phụ thuộc vào
kiểu được dùng để gọi throw. Để làm việc này
chúng ta phải viết dấu ba chấm thay vì kiểu và
tên số tham số:
try { // code here
}
catch (...) { cout << "Exception occurred";
}
Còn có thể lồng các khối try-catch vào các khối try khác. Trong trường
hợp này, một khối catch bên trong có thể
chuyển tiếp exception nhận được cho
khối bên ngoài, để làm việc này chúng ta sử
dụng biểu thức throw; không có tham số. Ví
dụ:
try { try { // code here
}
catch (int n) { throw;
}
}
catch (...) { cout << "Exception occurred";
}
Nếu một exception không
bị chặn bởi bất kì lệnh catch nào vì không có lệnh nào có
kiểu phù hợp, hàm đặc biệt terminate sẽ được
gọi.
Hàm này đã được định
nghĩa sẵn để chấm dứt chương trình
ngay lập tức và hiển thịc thông báo lỗi
"Abnormal termination". Dạng thức của nó như
sau:
void
terminate();
Một số hàm thuộc
thư viện C++ chuẩn gửi các exceptions mà chúng ta có
thể chặn nếu chúng ta sử dụng một
khối try. Những exceptions
này được gửi đi với kiểu tham số
là một lớp thừa kế từ std::exception. Lớp này (std::exception) được
định nghĩa trong file header C++ chuẩn <exception> và được
dùng làm mẫu cho hệ thống phân cấp các exception
chuẩn:
|
exception
|
|
bad_alloc
|
(gửi bởi new)
|
|
bad_cast
|
(gửi bởi dynamic_cast khi thất
bại với một kiểu tham chiếu)
|
|
bad_exception
|
(được gửi khi một
exception không phù hợp với lệnh catch nào)
|
|
bad_typeid
|
(gửi bởi typeid)
|
|
logic_error
|
|
|
 domain_error
|
|
|
 invalid_argument
|
|
|
 length_error
|
|
|
 out_of_range
|
|
|
runtime_error
|
|
|
 overflow_error
|
|
|
 range_error
|
|
|
 underflow_error
|
|
|
ios_base::failure
|
(gửi bởi ios::clear)
|
Bởi vì đây là một
hệ thống phân lớp có thứ bậc, nếu bạn
sử dụng một khối catch để chặn bất
kì một exception nào nằm trong hệ thông này bằng cách
sử dụng tham số biến (thêm một dấu &
vào phía trước tên của tham số) bạn sẽ
chặn được tất cả các exception thừa
kế (luật thừa kế trong C++)
Ví dụ dưới đây chặn một
exception có kiểu bad_typeid
(được
thừa kế từ exception), lỗi này được tạo ra khi
muốn biết kiểu của một con trỏ null.
// Những exception chuẩn #include <iostream.h> #include <exception> #include <typeinfo> class A {virtual f() {}; }; int main () { try { A * a = NULL; typeid (*a); } catch (std::exception& e) { cout << "Exception: " << e.what(); } return 0; } |
Exception: Attempted typeid of NULL pointer
|
Bạn có thể sử dụng các lớp
của hệ thống phân cấp các exception chuẩn này báo
những lỗi của mình hoặc thừa kế những
lớp mới từ chúng.