Xử Lý Ngoại Lệ trong C++

Chào bạn những người mơ ước trở thành lập trình viên! Hôm nay, chúng ta sẽ bắt đầu cuộc hành trình thú vị vào thế giới Xử Lý Ngoại Lệ trong C++. Như một giáo viên khoa học máy tính thân thiện, tôi đang ở đây để hướng dẫn bạn qua chủ đề quan trọng này. Hãy lấy ly cà phê yêu thích của bạn, thoải mái ngồi xuống và hãy cùng nhau bơi lội vào cuộc hành trình này!

C++ Exception Handling

Ngoại Lệ Là Gì?

Trước khi chúng ta bắt đầu ném và bắt ngoại lệ như những người biến mất chuyên nghiệp, hãy hiểu rõ những gì ngoại lệ là. Trong thế giới lập trình, ngoại lệ là những sự kiện bất ngờ xảy ra trong quá trình thực thi một chương trình. Chúng giống như những bài kiểm tra bất ngờ tôi đã từng đưa ra (xin lỗi về điều đó!) – bất ngờ và đôi khi có phần khó khăn để xử lý.

Ngoại lệ làm gián đoạn luồng thực thi bình thường của các hướng dẫn của chương trình. Chúng có thể bị gây ra bởi nhiều yếu tố khác nhau, chẳng hạn như:

  1. Chia cho số không
  2. Truy cập vượt quá phạm vi mảng
  3. Hết bộ nhớ
  4. Cố gắng mở một tệp không tồn tại

Bây giờ, hãy xem C++ cho phép chúng ta quản lý các tình huống bất ngờ này như thế nào một cách thanh tú.

Ném Ngoại Lệ

Cơ Bản Về Việc Ném

Trong C++, chúng ta sử dụng từ khóa throw để ném một ngoại lệ. Điều này giống như bạn đặt tay lên để hỏi một câu hỏi hoặc báo cáo một vấn đề trong lớp học. Dưới đây là một ví dụ đơn giản:

#include <iostream>
using namespace std;

int main() {
try {
throw 20;
}
catch (int e) {
cout << "Đã xảy ra ngoại lệ. Số thứ " << e << endl;
}
return 0;
}

Trong ví dụ này, chúng ta đang ném một ngoại lệ số nguyên với giá trị 20. Nhưng đừng lo, chúng ta sẽ bắt nó trong chốc lát!

Ném Các Loại Khác Nhau

C++ linh hoạt và cho phép bạn ném các ngoại lệ của các loại khác nhau. Hãy xem một ví dụ thực tế hơn:

#include <iostream>
#include <stdexcept>
using namespace std;

double chia(int a, int b) {
if (b == 0) {
throw runtime_error("Chia cho số không!");
}
return static_cast<double>(a) / b;
}

int main() {
try {
cout << chia(10, 2) << endl;  // Điều này sẽ hoạt động tốt
cout << chia(10, 0) << endl;  // Điều này sẽ ném một ngoại lệ
}
catch (const runtime_error& e) {
cout << "Đã bắt ngoại lệ: " << e.what() << endl;
}
return 0;
}

Trong ví dụ này, chúng ta đang ném một ngoại lệ runtime_error khi ai đó cố gắng chia cho số không. Điều này giống như đặt ra một dấu "Không Chia Cho Số Không" trong khu vực toán học của chúng ta!

Bắt Ngoại Lệ

Cơ Bản Về Việc Bắt

Bây giờ khi chúng ta biết cách ném ngoại lệ, hãy học cách bắt chúng. Việc bắt ngoại lệ như là một chủ chủ nuôi thú cưng có trách nhiệm – bạn cần phải chuẩn bị để xử lý bất kỳ điều gì mã của bạn ném ra!

Chúng ta sử dụng một khối try-catch để bắt ngoại lệ. Khối try chứa mã có thể ném ngoại lệ, và khối catch xử lý ngoại lệ nếu nó xảy ra.

#include <iostream>
using namespace std;

int main() {
try {
int tuoi = -5;
if (tuoi < 0) {
throw "Tuổi không thể âm!";
}
cout << "Tuổi là: " << tuoi << endl;
}
catch (const char* msg) {
cerr << "Lỗi: " << msg << endl;
}
return 0;
}

Trong ví dụ này, chúng ta đang kiểm tra nếu tuổi âm. Nếu vậy, chúng ta ném một ngoại lệ với thông điệp lỗi tùy chỉnh.

Bắt Nhiều Ngoại Lệ

Đôi khi, các loại ngoại lệ khác nhau có thể được ném từ cùng một đoạn mã. Trong những trường hợp này, chúng ta có thể có nhiều khối catch:

#include <iostream>
#include <stdexcept>
using namespace std;

int main() {
try {
int lua_chon;
cout << "Nhập 1 cho ngoại lệ số nguyên, 2 cho lỗi runtime: ";
cin >> lua_chon;

if (lua_chon == 1) {
throw 42;
} else if (lua_chon == 2) {
throw runtime_error("Một lỗi runtime bất ngờ xuất hiện!");
} else {
throw "Lựa chọn không xác định!";
}
}
catch (int e) {
cout << "Đã bắt ngoại lệ số nguyên: " << e << endl;
}
catch (const runtime_error& e) {
cout << "Đã bắt lỗi runtime: " << e.what() << endl;
}
catch (...) {
cout << "Đã bắt một ngoại lệ không xác định!" << endl;
}
return 0;
}

Ví dụ này cho thấy cách chúng ta có thể bắt các loại ngoại lệ khác nhau. Khối catch (...) là một khối bắt tất cả sẽ xử lý bất kỳ ngoại lệ nào không được bắt bởi các khối catch trước đó. Điều này như có một mạng an toàn cho tất cả những điều bất ngờ!

Ngoại Lệ Tiêu Chuẩn C++

C++ đi kèm với một bộ ngoại lệ tiêu chuẩn mà bạn có thể sử dụng trong các chương trình của mình. Những ngoại lệ này như những chiếc dao cơ khổng lồ của thế giới ngoại lệ – linh hoạt và luôn sẵn sàng để giúp đỡ!

Dưới đây là bảng một số ngoại lệ tiêu chuẩn thường được sử dụng:

Ngoại Lệ Mô Tả
std::runtime_error Lỗi logic runtime
std::logic_error Lỗi logic
std::out_of_range Truy cập vượt quá phạm vi
std::overflow_error Tràn số
std::bad_alloc Không thể phân bổ bộ nhớ

Hãy xem một ví dụ sử dụng một ngoại lệ tiêu chuẩn:

#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;

int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
try {
cout << numbers.at(10) << endl;  // Điều này sẽ ném một ngoại lệ out_of_range
}
catch (const out_of_range& e) {
cerr << "Lỗi out_of_range: " << e.what() << endl;
}
return 0;
}

Trong ví dụ này, chúng ta đang cố gắng truy cập một phần tử ngoài phạm vi trong vector của chúng ta. Hàm at() sẽ ném một ngoại lệ out_of_range khi điều này xảy ra.

Định Nghĩa Ngoại Lệ Mới

Mặc dù các ngoại lệ tiêu chuẩn rất tốt, đôi khi bạn cần điều gì đó phù hợp hơn với nhu cầu cụ thể của bạn. Đó là khi ngoại lệ tùy chỉnh có thể đến từ thiện!

Dưới đây là cách bạn có thể định nghĩa lớp ngoại lệ của riêng bạn:

#include <iostream>
#include <exception>
using namespace std;

class NegativeValueException : public exception {
public:
const char* what() const throw() {
return "Giá trị âm không được phép!";
}
};

double can_bac_hai(double x) {
if (x < 0) {
throw NegativeValueException();
}
return sqrt(x);
}

int main() {
try {
cout << can_bac_hai(25) << endl;  // Điều này sẽ hoạt động tốt
cout << can_bac_hai(-5) << endl;  // Điều này sẽ ném ngoại lệ tùy chỉnh của chúng ta
}
catch (const NegativeValueException& e) {
cerr << "Lỗi: " << e.what() << endl;
}
return 0;
}

Trong ví dụ này, chúng ta đã tạo một lớp NegativeValueException tùy chỉnh. Chúng ta sử dụng nó trong hàm can_bac_hai để ném một ngoại lệ khi ai đó cố gắng tính căn bậc hai của một số âm.

Và thế là, các bạn đã vượt qua các khái niệm cơ bản về Xử Lý Ngoại Lệ trong C++. Hãy nhớ rằng, ngoại lệ là những người bạn. Chúng giúp bạn viết mã dẫn động và chống lỗi tốt hơn. Hãy tiếp tục tập luyện, và may mắn bạn sẽ luôn bắt được những ngoại lệ!

Chúc các bạn lập trình vui vẻ, và may những ngoại lệ của bạn luôn được bắt!

Credits: Image by storyset