Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C++

Khối catch và chuyển đổi kiểu trong C++

Trong C++, khi ném (throw) một đối tượng, khối catch khớp dựa trên kiểu tĩnh của đối tượng được ném, không thực hiện chuyển đổi kiểu ngầm định như ép kiểu số hoặc gọi constructor chuyển đổi.

Ví dụ 1: Không chuyển đổi char sang int

#include <iostream>

using namespace std;

int main() {
    try {
        throw 'a';  // ném ký tự 'a'
    }
    catch (int a) {
        cout << "Bắt int: " << a;
    }
    catch (...) {
        cout << "Vào catch mặc định";
    }
}

Kết quả

Vào catch mặc định

Giải thích: Dù 'a' có mã ASCII là 97 (int), nhưng C++ không chuyển đổi char sang int trong cơ chế bắt ngoại lệ. Khối catch(int) không khớp với throw 'a'.

Ví dụ 2: Không gọi constructor chuyển đổi

#include <iostream>

using namespace std;

class TestExcept1 {};
class TestExcept2 {
public:
    TestExcept2(const TestExcept1& e) {  // Constructor chuyển đổi
        cout << "Constructor chuyển đổi được gọi";
    }
};

int main() {
    try {
        TestExcept1 exp1;
        throw exp1;  // ném đối tượng TestExcept1
    }
    catch (TestExcept2 e2) {
        cout << "Bắt TestExcept2" << endl;
    }
    catch (...) {
        cout << "Vào catch mặc định" << endl;
    }
}

Kết quả

Vào catch mặc định

Giải thích: Dù có constructor chuyển đổi từ TestExcept1 sang TestExcept2, cơ chế throw/catch không gọi constructor này. Khối catch(TestExcept2) không khớp.

Quy tắc khớp catch

  1. Khớp chính xác kiểu (exact match)
  2. Khớp với lớp cơ sở công khai (public base class) — chỉ cho con trỏ/tham chiếu
  3. Không chuyển đổi kiểu số (int ↔ double, char ↔ int...)
  4. Không gọi constructor chuyển đổi
  5. Không ép kiểu const/volatile

Ví dụ 3: Đối tượng dẫn xuất ném như cơ sở (với con trỏ)

class Base { virtual ~Base() {} };
class Derived : public Base {};

int main() {
    try {
        Derived d;
        throw d;  // ném đối tượng Derived (bị slice thành Base nếu catch by value)
    }
    catch (Base& b) {  // Bắt bằng tham chiếu - OK, không slice
        // b tham chiếu đến đối tượng Derived gốc
    }
    catch (Base b) {   // Bắt bằng giá trị - SLICE xảy ra!
        // b chỉ là phần Base của Derived
    }
}

Kết luận

  • Luôn bắt ngoại lệ bằng tham chiếu const: catch (const MyException& e)
  • Không mong đợi chuyển đổi kiểu ngầm trong catch
  • Ném đối tượng (by value), bắt bằng tham chiếu (by reference)