2017-12-27 114 views
16
#include <fstream> 
#include <iostream> 
#include <map> 

int main(int argc, char** argv) { 
    try { 
     std::map<std::string, int> m{{"a", 1}, {"b", 2}}; 
     std::cout << m.at("c") << std::endl; 
    } catch (const std::exception& e) { 
     std::cerr << e.what() << std::endl; 
    } 

    return 0; 
} 

Trong C++, khi truy xuất khóa không tồn tại của bản đồ, ngoại lệ trông giống như map::at: key not found. Thông tin về khóa không được cung cấp.Tại sao ngoại lệ của C++ không cung cấp chi tiết cuộc gọi?

Ngoài ra, nếu một người đang truy cập tệp không tồn tại, thông báo ngoại lệ của std::ios_base::failure trông giống như ios_base::clear: unspecified iostream_category error. Tên tệp gây ra ngoại lệ không được cung cấp. Vì vậy, có thể mất khá nhiều thời gian để tìm ra nơi ngoại lệ là từ nếu có nhiều việc sử dụng map.at() hoặc ifstream is trong một dự án.

Ngược lại, Python có thể cho bạn biết KeyError: 'c' hoặc FileNotFoundError: [Errno 2] No such file or directory: 'foo'.

Đây có phải là quy ước C++ không? Cảm ơn bạn.

+20

Hãy nhớ rằng, trong C++, chìa khóa của một bản đồ có thể không được chuyển đổi thành một chuỗi. – nos

+0

Các C# FileNotFoundException cũng không đề cập đến tên tập tin của tập tin mà không thể được tìm thấy –

+1

@ nos: theo tinh thần của stdlib bạn có lẽ nên nói "không có một op <<" nhưng thậm chí sau đó người ta có thể tranh luận rằng nó nên có thể làm điều này cho tất cả những loại mà nó có thể. Imho lý do chính là mạnh mẽ: những gì sẽ xảy ra nếu trong khi cố gắng để lắp ráp một thông báo lỗi đẹp và dandy, bạn gặp phải một ngoại lệ? – PlasmaHH

Trả lời

37

Vấn đề là mô hình đối tượng C++, khác với mô hình của Python. Ngược lại, trước tiên hãy trả lời: Python lưu trữ những gì trong đối tượng ngoại lệ để in khóa? Đó là một tài liệu tham khảo giữ cho đối tượng đó còn sống.

Điều này không thể thực hiện đơn giản bằng C++.

  1. std::out_of_range không thể lưu con trỏ hoặc tham chiếu đến khóa như nguyên trạng. Người xử lý cho trường hợp ngoại lệ có thể ở rất xa. Và điều đó có nghĩa là chìa khóa nhất có lẽ đã đi ra khỏi phạm vi trước khi xử lý được nhập vào. Chúng tôi nhận được hành vi không xác định nếu khóa được gọi.

  2. std::out_of_range là lớp bê tông. Không phải mẫu như std::map. Nó không thể dễ dàng sao chép chìa khóa vào chính nó. Có nhiều loại khác nhau là Key và rõ ràng là không thể giải thích cho tất cả chúng. Ngay cả khi có thể, điều gì sẽ xảy ra nếu khóa cực kỳ tốn kém để sao chép? Hoặc thậm chí không thể sao chép được? Ngay cả trong trường hợp điều đó không đúng, điều gì sẽ xảy ra nếu khóa không thể chuyển đổi thành chuỗi hoặc có thể in được?

Các điểm trên không có nghĩa là không thể. std::map::at có thể ném một lớp con của std::out_of_range mà loại xóa và vv. Nhưng tôi hy vọng bạn thấy nó có chi phí không tầm thường. C++ là tất cả về việc không trả tiền trong hiệu suất cho các tính năng bạn không cần hoặc sử dụng. Làm cho tất cả mọi người chịu rằng vô điều kiện trên không phải là phù hợp với thiết kế này.

3

Chuẩn C++ chỉ định rằng map::at(const key_type& k) sẽ khởi chạy một ngoại lệ std::out_of_range (nếu giá trị không nằm trong đó); không có gì hơn ... nhưng không có gì ít hơn; thực hiện cụ thể map::at() nhưng vấn đề sẽ là key_type của k phải được chuyển thành char *. Vì vậy, có một vài lựa chọn:

  1. Không hiển thị thông tin này
  2. std::map không lưu trữ các loại mà không cần (ít nhất là implicit) chuyển đổi để char *: nhưng sẽ có một yêu cầu áp dụng cho các loại key_store đó là không chặt chẽ cần thiết
  3. Để cung cấp thông điệp khác nhau trong std::out_of_range ngoại lệ tùy thuộc vào key_type: nhưng giải pháp này không phải lúc nào cũng hiển thị thông tin dự kiến ​​

trong điểm khác xem, std::out_of_range được kế thừa từ std::logic_error; Tiêu chuẩn C++ phân biệt giữa hai loại ngoại lệ chính:

  • logic_error: chúng do lỗi trong logic nội bộ của chương trình . Về lý thuyết, chúng có thể ngăn ngừa được.
  • thời gian chạy_error: chúng là do các sự kiện nằm ngoài phạm vi của chương trình. Họ không thể dễ dàng dự đoán trước.

Trong trường hợp của chúng tôi, chúng tôi có thể kiểm tra sự tồn tại của một phần tử một cách dễ dàng (vì vậy trường hợp của chúng tôi sửa chữa với trường hợp này)

0

Với C++, nó có thể tạo ra một ngoại lệ cá nhân kế thừa từ std :: ngoại lệ với thông tin bổ sung, như tên tệp. Dưới đây là một ví dụ:

#include <sstream> 

class MyException : public std::exception { 
    public : 
    MyException (std::exception e, std::string filename = "") : std::exception (e), str_ (filename) {} 
    MyException (const MyException& e) : std::exception (e), str_ (e.str_) {} 
    const char* what() const throw() { 
    std::ostringstream oss; 
    oss << "exception occured in " << str_ << " : "; 
    oss << std::exception::what(); 
    return oss.str().c_str(); 
    } 
    std::string str_; 
}; 

int main(int argc, char** argv) { 
    try { 
     try { 
      std::map<std::string, int> m{{"a", 1}, {"b", 2}}; 
      std::cout << m.at("c") << std::endl; 
     } 
     catch (const std::exception& e) { 
      MyException f (e, "main"); 
      throw (f); 
     } 
    } 
    catch (const MyException& e) { 
     std::cerr << e.what() << std::endl; 
    } 

    return 0; 
} 

sẽ hiển thị:

exception occured in main : std::exception 
Các vấn đề liên quan