2009-02-18 57 views
25

Có cách nào để xác định loại ngoại lệ ngay cả khi biết bạn đã bắt được ngoại lệ với việc bắt tất cả không?Xác định loại ngoại lệ sau khi ngoại lệ bị bắt?

Ví dụ:

try 
{ 
    SomeBigFunction(); 
} 
catch(...) 
{ 
    //Determine exception type here 
} 
+0

u có thể giải thích tại sao bạn cần nó? có lẽ chúng ta có thể tìm kiếm các lựa chọn thay thế? –

+0

Tôi rõ ràng không bao giờ làm điều này mã hóa một cái gì đó từ đầu, nhưng theo những hoàn cảnh cụ thể tôi dưới nó sẽ hữu ích. Mã kế thừa. –

Trả lời

19

Bạn actully có thể xác định loại bên trong một catch (...), nhưng nó không phải là rất hữu ích:

#include <iostream> 
#include <exception> 

    class E1 : public std::exception {}; 
    class E2 : public std::exception {}; 

    int main() { 
     try { 
      throw E2(); 
     } 
     catch(...) { 
      try { 
       throw; 
      } 
      catch(const E1 & e) { 
       std::cout << "E1\n"; 
      } 
      catch(const E2 & e) { 
       std::cout << "E2\n"; 
      } 
     } 
    } 
+3

Kỹ thuật này thực sự CÓ THỂ hữu ích nếu 'try {throw; } '... phần nằm trong một hàm được gọi trong khối' catch (...) '. – wjl

+1

Kỹ thuật này * rất hữu ích. Hãy nghĩ đến việc gọi một trình xử lý ngoại lệ trong 'catch (...)'. Trình xử lý ngoại lệ lại ném và xác định kiểu với kỹ thuật này. Điều này cho phép tách một đường dẫn mã bình thường khỏi đường dẫn mã bất thường (ngoại lệ). – user23573

1

số

Làm như vậy sẽ ít nhất yêu cầu bạn để có thể truy cập vào các ngoại lệ hiện hành. Tôi không tin rằng có một cách tiêu chuẩn để làm điều này.

Khi bạn đã có trường hợp ngoại lệ, bạn sẽ phải sử dụng thuật toán kiểm tra loại. C++ không có hỗ trợ vốn có cho việc này. Tốt nhất bạn sẽ phải có một tuyên bố if/elseif lớn với dynamic_cast's để kiểm tra kiểu.

5

Nếu bạn cần xử lý các ngoại lệ khác nhau dựa trên những gì họ đang có, bạn nên bắt ngoại lệ cụ thể. Nếu có các nhóm ngoại lệ mà tất cả cần phải được xử lý giống hệt nhau, lấy chúng từ một lớp cơ sở chung và bắt lớp cơ sở sẽ là con đường để đi. Tận dụng sức mạnh và mô hình của ngôn ngữ, không chiến đấu chống lại chúng!

25

ngắn Trả lời: Không

dài trả lời:

Nếu bạn lấy được tất cả các trường hợp ngoại lệ của bạn từ một loại cơ sở chung (nói std :: exception) và bắt này một cách rõ ràng thì bạn có thể sử dụng điều này để có được loại thông tin từ ngoại lệ của bạn.

Nhưng bạn nên sử dụng tính năng bắt để bắt làm loại ngoại lệ cụ thể và sau đó làm việc từ đó.

Việc sử dụng thực chỉ dành cho catch (...) là:

  • Catch: và vứt bỏ ngoại lệ (stop ngoại lệ thoát destructor).
  • Catch: Đăng nhập một ngoại lệ không rõ ràng happend và tái ném.

được sửa đổi: Bạn có thể trích xuất thông tin loại qua dynamic_cast <>() hoặc qua typid() Mặc dù như đã nêu trên đây không phải là somthing tôi khuyên bạn nên. Sử dụng báo cáo trường hợp.

#include <stdexcept> 
#include <iostream> 

class X: public std::runtime_error // I use runtime_error a lot 
{         // its derived from std::exception 
    public:       // And has an implementation of what() 
     X(std::string const& msg): 
      runtime_error(msg) 
     {} 
}; 

int main() 
{ 
    try 
    { 
     throw X("Test"); 
    } 
    catch(std::exception const& e) 
    { 
     std::cout << "Message: " << e.what() << "\n"; 

     /* 
     * Note this is platform/compiler specific 
     * Your milage may very 
     */ 
     std::cout << "Type: " << typeid(e).name() << "\n"; 
    } 
} 
+0

Làm thế nào mà sẽ làm việc trong trường hợp này với một bắt tất cả? – JaredPar

+0

Được chỉnh sửa để làm cho rõ ràng hơn rằng bạn sẽ cần phải nắm bắt ngoại lệ cơ bản. –

+0

"sau đó bạn có thể sử dụng điều này để có được thông tin kiểu từ ngoại lệ của bạn.". Làm sao? Thông qua một cái gì đó giống như một dynamic_cast? –

-2

Nếu bạn đang sử dụng Visual C++ (quản lý), bạn có thể sử dụng GetType() phương pháp để có được loại ngoại lệ và xử lý nó từ đó.

Ví dụ:

try 
    { 
     // Run the application 
     Application::Run(mainForm); 
    } 
    catch (Exception^ e) 
    { 
     String^ exception_type = e->GetType()->ToString(); 
     throw; 
    } 

Chuỗi sẽ chứa nội dung như "System.ArgumentOutOfRangeException".

+5

Nó không phải là C++. Đó là C++/CLI. Mà chủ yếu chỉ là một cú pháp C++ cho thời gian chạy .NET. Nó cung cấp chức năng C++, nhưng phần CLI hoàn toàn khác với phần C++. – Eonil

0

Tôi đã thử nhiều cách khác nhau; công trình này cho tôi:

Bắt đầu bằng cách subclassing runtime_error:

/*----------------------------------------------------------------------*/  
/* subclass runtime_error for safe exceptions in try/throw/catch  */ 

#include <stdexcept> 
/* a little preprocessor magic here -- makes a subclass of runtime_error*/ 

#define NEWERROR(NE) class NE : public runtime_error {    \ 
     public: NE (string const& error) : runtime_error(error) {} } 


NEWERROR(FileError  ); 
NEWERROR(NetworkError ); 
NEWERROR(StringError ); 
NEWERROR(CofeeError ); 

/*----------------------------------------------------------------------*/ 

Sau đó, bạn có thể tạo ra một số trường hợp ngoại lệ của bạn.

/*----------------------------------------------------------------------*/ 
/* some example pre-defined exceptions */ 

FileError  ReadOnly    ("ReadOnly"   ); 
FileError  FileNotFound   ("FileNotFound"  ); 
NetworkError TimeOutExceeded   ("TimeOutExceeded"  ); 
NetworkError HostNotFound   ("HostNotFound"  ); 
CoffeeError OutOfCoffee    ("OutOfCoffee"   ); 

/*----------------------------------------------------------------------*/ 

Rõ ràng thông báo cho trình biên dịch rằng chức năng của bạn có thể ném một ngoại lệ hoặc chương trình có thể sẽ chấm dứt tại điểm ném, và dữ liệu có thể bị mất hoặc bị hỏng nếu nguồn lực đang được sử dụng vào thời điểm đó.

"Đảm bảo bạn có thể và bắt bất cứ thứ gì bạn có thể ném."

(tôi sử dụng generic runtime_error vì ném và bắt nó bao gồm tất cả các trường hợp ngoại lệ của tôi cộng với những người của hệ thống là tốt.)

/*----------------------------------------------------------------------*/ 
/* example function that may throw an exception */ 

#include <fstream> 

ifstream& getFileStream (string fname) throw (runtime_error) 
{ 

    if (fname == "") 
     throw StringError("<getFileStream> fname:empty string"); 
     // processing stops here if thrown 

    try 
     { 
     ifstream Inputfstream; 

     ifstream& ifsref = Inputfstream; 

     // ifstream has its own <legacy> exception 
     // mechanisms and procedures 
     ifsref.exceptions (ifstream::failbit | ifstream::badbit); 

     ifsref.open (fname , ifstream::in); // could fail ==> ifstream::failure exception 
     } 
    catch (ifstream::failure e) 
     { 
     throw FileError(fname + string(e.what())); 
     } 

    return ifsref; 
} 

/*----------------------------------------------------------------------*/ 

sau đó trong thử của bạn/catch

/*----------------------------------------------------------------------*/ 
catch (FileNotFound fnf) //catch a specific error 
{ 
    if (DEBUG) cerr << "[File Not Found Error: " << fnf.what() << "]" << endl; 
    ... (handle it) ... 
} 
catch (FileError fe) //catch a specific type 
{ 
    if (DEBUG) cerr << "[File Error: " << fe.what() << "]" << endl; 
    ... (handle it) ... 
} 
catch (runtime_error re) // catch a generic type 
{ 
    if (DEBUG) cerr << "[Runtime error: " << re.what() << "]" << endl;   

    // determine type by string comparison 
    if (re.what() == string("ResourceNotavailable")) ... 
    if (re.what() == string("NetWorkError")  ) ... 

    ... 

} 
catch (...) // catch everything else 
{ ... exit, rethrow, or ignore ... } 

/*----------------------------------------------------------------------*/ 

Lớp thời gian chạy lỗi có hỗ trợ tốt trong thư viện chuẩn C++, và trình biên dịch biết về nội bộ và cách tối ưu hóa bộ nhớ và công văn, để bạn có thể sử dụng chúng trên các cơ sở mã khác nhau một cách an toàn và tự tin. Mã này là xách tay và tương thích với nhiều trình biên dịch và kiến ​​trúc khác nhau. Có thể thích hợp hơn và nhanh hơn khi bắt từng lỗi riêng biệt trong một điều khoản bắt, từ cụ thể hơn đến chung hơn, nếu bạn cảm thấy một chuỗi các kết quả chuỗi là một sự lãng phí khủng khiếp của cpu và bộ nhớ.).

<stdexcept> cung cấp cho bạn một số loại ngoại lệ trong 2 nhóm:

  • logic lỗi:

    logic_error 
    domain_error 
    invalid_argument 
    length_error 
    out_of_range 
    
  • lỗi Runtime:

    runtime_error 
    range_error 
    overflow_error 
    underflow_error 
    

cú pháp sử dụng hơi khác nhau đối với một số người trong số họ.

Wisdom thông thường trong C++ nói rằng trường hợp ngoại lệ của bạn nên được tương đối "phẳng", có nghĩa là hệ thống phân cấp lớn các loại cụ thể của trường hợp ngoại lệ nên tránh làm trong ủng hộ của những người thân chung nhưng thông tin ngắn cho công việc lập trình nói chung. Các nhiệm vụ cụ thể của miền như logic hệ thống mạng, toán học cao hơn, v.v. có thể được hưởng lợi từ tính đặc hiệu, nhưng điều đó có thể đạt được bằng cách tạo các chuỗi lỗi thông minh với các ngoại lệ thời gian chạy/logic chung.

Cuối cùng, điểm của tôi là: Bạn có thể đạt được tất cả điều này bằng cách ném và chỉ bắt runtime_error.

Bạn không cần phải tạo ra toàn bộ các túi ngoại lệ đặc biệt cao (giống như java) cho mỗi lớp, mỗi xử lý một lỗi cụ thể.

+0

Hmm .. chỉ định loại ngoại lệ mà phương thức ném không được chấp nhận trong một thời gian. –

+0

Có, vì vậy nó nói. Nhưng thực tế có một số vấn đề có thể không rõ ràng khi không chỉ định loại ngoại lệ. Vấn đề là trong thực tế, bất kỳ phương pháp nào cũng có thể ném bất cứ thứ gì: int, char *, objects,… Nếu bạn chỉ định phương thức X ném (A, B, C) và sau đó nó ném cái gì khác (như lỗi malloc) std :: bất ngờ được gọi thay vì tìm kiếm một phương thức xử lý hoặc gọi std :: chấm dứt. Hoặc nếu bạn chỉ khai báo một phương thức với một mệnh đề throw() rỗng: std :: unexpected được gọi là bất kỳ ngoại lệ nào. –

1

Không có cách nào tiêu chuẩn, di động để thực hiện việc này. Đây là một cách không cầm tay để làm điều đó trên GCC và kêu vang

#include <iostream> 
#include <cxxabi.h> 

const char* currentExceptionTypeName() 
{ 
    int status; 
    return abi::__cxa_demangle(abi::__cxa_current_exception_type()->name(), 0, 0, &status); 
} 

int main() 
{ 
    try { 
     throw std::string(); 
    } catch (...) { 
     std::cout<<"Type of caught exception is "<<currentExceptionTypeName()<<std::endl; 
    } 

    return 0; 
} 

Output:

Type of caught exception is std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > 
0

Câu hỏi này đã được hỏi một số thời gian trước đây và tôi đang cung cấp câu trả lời này là người bạn đồng hành với các câu trả lời được chấp nhận từ 9 năm trước. Tôi phải đồng ý với người trả lời rằng câu trả lời đó, "... không hữu ích lắm." Hơn nữa, nó mở cửa cho một ngoại lệ mà đã từng được xử lý không được giải quyết. Để minh họa, hãy để tôi xây dựng dựa trên câu trả lời của bị đơn

#include <iostream> 
#include <exception> 

class E1 : public std::exception {}; 
class E2 : public std::exception {}; 
class E3 : public std::exception {}; 

int main() { 
    try { 
     throw E3(); 
    } 
    catch(...) { 
     try { 
      // OOOPS!!! E3 is now unhandled!!!!!! 
      throw; 
     } 
     catch(const E1 & e) { 
      std::cout << "E1\n"; 
     } 
     catch(const E2 & e) { 
      std::cout << "E2\n"; 
     } 
    } 
} 

Một thay thế cho phương pháp này sẽ là như sau:

#include <iostream> 
#include <exception> 

class E1 : public std::exception {}; 
class E2 : public std::exception {}; 
class E3 : public std::exception {}; 

int main() { 
    try { 
     throw E3(); 
    } 
    catch(const E1 & e) { 
     std::cout << "E1\n"; 
    } 
    catch(const E2 & e) { 
     std::cout << "E2\n"; 
    } 
    catch(...) { 
     std::cout << "Catch-all..."; 
    } 
} 

tiếp cận thứ hai này có vẻ là tương đương với người đầu tiên và có lợi thế đặc biệt xử lý E1E2 và sau đó bắt mọi thứ khác. Điều này được cung cấp chỉ như là một thay thế.

Xin lưu ý rằng, theo C++ Dự thảo 2011/02/28, đoạn 15.3, đạn mục 5, "Nếu hiện tại, một ... xử lý sẽ xử lý cuối cùng cho khối try của nó."

Các vấn đề liên quan