C ++ nắm bắt mọi ngoại lệ


242

Có tương đương với c ++ của Java không

try {
    ...
}
catch (Throwable t) {
    ...
}

Tôi đang cố gắng gỡ lỗi mã Java / jni gọi các hàm windows gốc và máy ảo tiếp tục gặp sự cố. Mã riêng xuất hiện tốt trong thử nghiệm đơn vị và dường như chỉ bị sập khi được gọi thông qua jni. Một cơ chế bắt ngoại lệ chung sẽ chứng minh cực kỳ hữu ích.



2
Lưu ý rằng hầu hết các sự cố không phải do ngoại lệ trong C ++. Bạn có thể bắt tất cả các ngoại lệ, nhưng điều đó sẽ không ngăn được nhiều sự cố.
Vịt Mooing

Câu trả lời:


333
try{
    // ...
} catch (...) {
    // ...
}

sẽ bắt tất cả các ngoại lệ C ++, nhưng nó nên được coi là thiết kế xấu. Bạn có thể sử dụng cơ chế current_exception mới của c ++ 11, nhưng nếu bạn không có khả năng sử dụng c ++ 11 (hệ thống mã kế thừa yêu cầu viết lại), thì bạn không có con trỏ ngoại lệ được đặt tên để sử dụng để nhận thông điệp hoặc tên . Bạn có thể muốn thêm các mệnh đề bắt riêng cho các trường hợp ngoại lệ khác nhau mà bạn có thể bắt và chỉ bắt mọi thứ ở phía dưới để ghi lại một ngoại lệ không mong muốn. Ví dụ:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}

68
Đó là một thực hành tốt để bắt ngoại lệ bằng cách tham chiếu const. Như trong: Catch (std :: ex const & ex) {/ * ... * /}
coryan

12
@coryan: Tại sao nên bắt bằng cách tham khảo const?
Tim MB

19
Tránh các bản sao không cần thiết là một lợi ích.
Greg D

21
-1: gợi ý rằng điều này sẽ "bắt tất cả các ngoại lệ trong C ++" là sai lệch. Hãy thử tạo một số chia cho lỗi không trong khối thử. Bạn sẽ thấy rằng nó sẽ tạo ra một ngoại lệ không bị bắt, nhưng mã rõ ràng là trong C ++. Sẽ hữu ích hơn khi tuyên bố rằng điều này sẽ "bắt tất cả các ngoại lệ C ++" và sau đó thêm một số đề cập đến các ngoại lệ có cấu trúc vào các ghi chú về tính hữu dụng hạn chế.
omatai

42
@omatai: Đã sửa, nó sẽ bắt tất cả các ngoại lệ C ++. Chia cho số 0 là hành vi không xác định và không tạo ra ngoại lệ C ++.
Vịt Mooing

150

Ai đó nên thêm rằng người ta không thể bắt được "sự cố" trong mã C ++. Những người không ném ngoại lệ, nhưng làm bất cứ điều gì họ thích. Khi bạn thấy một chương trình bị sập vì nói rằng một sự vô hiệu của con trỏ null, nó đang thực hiện hành vi không xác định. Không có std::null_pointer_exception. Cố gắng để bắt ngoại lệ sẽ không giúp đỡ ở đó.

Chỉ trong trường hợp ai đó đang đọc chủ đề này và nghĩ rằng anh ta có thể có được nguyên nhân của sự cố chương trình. Một trình gỡ lỗi như gdb nên được sử dụng thay thế.


4
Vâng, như Shy chỉ ra, có thể với trình biên dịch VC. Đó không phải là một ý tưởng tốt, nhưng nó có thể.
Shog9

7
vâng với SEH. nhưng không phải với các kỹ thuật c ++ tiêu chuẩn lành mạnh :) tốt nếu bạn bám vào các cửa sổ, bạn gần như có thể làm mọi thứ :)
Johannes Schaub - litb

1
Mmm ... cảm ơn vì miếng ngon này. Tôi đã tìm kiếm câu trả lời về lý do tại sao các ngoại lệ con trỏ null của tôi không bị bắt!
Dalin Seivewright

10
Bạn có thể bắt gặp các lỗi phân tách với SEH trên Windows và tín hiệu (2) / sigaction (2) trên các hệ thống POSIX, bao gồm phần lớn các hệ thống đang sử dụng hiện nay, nhưng giống như xử lý ngoại lệ, đó không phải là thứ nên được sử dụng cho điều khiển luồng thông thường. Đó là nhiều hơn một "làm một cái gì đó hữu ích trước khi chết."
Adam Rosenfield

1
@AdamRosenfield cho đến khi bạn thực hiện try { .. } catch(...) { ... }bắt bằng cách sử dụng tín hiệu / sigaction, tôi sẽ không gọi nó là "bắt" :) Nếu trong trình xử lý tín hiệu, lập trình viên tương đối khó biết được sự cố xảy ra trong đoạn mã nào (tôi đang nói về lập trình phát hiện điều đó), so với thử / bắt.
Julian Schaub - litb

72

Đây là cách bạn có thể thiết kế ngược loại ngoại lệ từ bên trong catch(...)nếu bạn cần (có thể hữu ích khi bắt không biết từ thư viện bên thứ ba) với GCC:

#include <iostream>

#include <exception>
#include <typeinfo>
#include <stdexcept>

int main()
{
    try {
        throw ...; // throw something
    }
    catch(...)
    {
        std::exception_ptr p = std::current_exception();
        std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
    }
    return 1;
}

và nếu bạn có đủ khả năng sử dụng Boost, bạn có thể làm cho phần bắt của mình trở nên đơn giản hơn (ở bên ngoài) và có khả năng đa nền tảng

catch (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}

58
try {
   // ...
} catch (...) {
   // ...
}

Lưu ý rằng ...bên trong catchlà một dấu chấm lửng thực, tức là. Ba chấm.

Tuy nhiên, vì các ngoại lệ C ++ không nhất thiết là các lớp con của Exceptionlớp cơ sở , nên không có cách nào để thực sự thấy biến ngoại lệ được ném khi sử dụng cấu trúc này.


24
Trong C ++ 11 có: thử {std :: string (). At (1); // điều này tạo ra một std :: out_of_range} Catch (...) {eptr = std :: current_exception (); // chụp}
Mohammad Alaggan

2
@bfontaine: Vâng, nhưng tôi đã nói rằng để phân biệt trình catchxác định với trình giữ chỗ mã hiện có trong một nhận xét ( // ...) rõ ràng không phải là cú pháp C ++.
Greg Hewgill

1
@GregHewgill: vâng, đó chỉ là lỗi chính tả.
bfontaine

1
@bfontaine: Đủ công bằng. :)
Greg Hewgill

43

không thể (trong C ++) để bắt tất cả các ngoại lệ theo cách di động. Điều này là do một số ngoại lệ không phải là ngoại lệ trong ngữ cảnh C ++. Điều này bao gồm những thứ như chia cho các lỗi không và những thứ khác. Có thể hack về và do đó có khả năng đưa ra các ngoại lệ khi những lỗi này xảy ra, nhưng nó không dễ thực hiện và chắc chắn không dễ để có được ngay trong một cách di động.

Nếu bạn muốn bắt tất cả các ngoại lệ STL, bạn có thể làm

try { ... } catch( const std::exception &e) { ... }

Cái nào sẽ cho phép bạn sử dụng e.what(), cái này sẽ trả về a const char*, cái này có thể cho bạn biết thêm về chính ngoại lệ đó. Đây là cấu trúc giống với cấu trúc Java, bạn đã hỏi về nhiều nhất.

Điều này sẽ không giúp bạn nếu ai đó đủ ngu ngốc để ném một ngoại lệ không được thừa hưởng std::exception.


1
Tại sao điều này không nằm trong top?
Ivan Sanz-Carasa

30

Tóm lại, sử dụng catch(...). Tuy nhiên, lưu ý rằng catch(...)có nghĩa là được sử dụng cùng với throw;về cơ bản:

try{
    foo = new Foo;
    bar = new Bar;
}
catch(...)       // will catch all possible errors thrown. 
{ 
    delete foo;
    delete bar;
    throw;       // throw the same error again to be handled somewhere else
}

Đây là cách thích hợp để sử dụng catch(...).


6
tốt hơn hết là sử dụng RAII để quản lý bộ nhớ tự động xử lý các tình huống ngoại lệ này.
paykoob

1
@paykoob Làm thế nào để xử lý các trường hợp bạn đã tạo để tạo một foo mới nhưng nó đã thất bại trên một thanh. Hoặc khi hàm tạo của thanh trys mở tệp nhưng không thành công và do đó ném. sau đó bạn có thể kết thúc với một foo
dangling

2
@MelleSterk Không phải ngăn xếp vẫn được dọn sạch trong trường hợp đó, mà sẽ chạy hàm Foohủy của nó? Tôi nghĩ đó là toàn bộ quan điểm của RAII. Tuy nhiên, nếu bạn cần một con trỏ Foothay vì chỉ tạo ra Foongăn xếp, thì bạn cần phải bọc con trỏ trong một thứ khác được khai báo trên ngăn xếp.
thiệu lại

có tự động foo = std :: make_unique <Foo> (); thanh tự động = std :: make_unique <Bar> (); // là ngoại lệ an toàn và sẽ không bị rò rỉ, không bắt (...) bắt buộc
paulm

Câu trả lời này xứng đáng được bình chọn nếu chỉ dành cho cuộc thảo luận đã bắt đầu :)
Cristik

21

có thể làm điều này bằng cách viết:

try
{
  //.......
}
catch(...) // <<- catch all
{
  //.......
}

Nhưng có một rủi ro rất đáng chú ý ở đây: bạn không thể tìm thấy loại lỗi chính xác đã bị ném trong trykhối, vì vậy hãy sử dụng loại này catchkhi bạn chắc chắn rằng bất kể loại ngoại lệ nào, chương trình phải tồn tại theo cách được xác định trong catchkhối.


31
Tôi hy vọng bạn có một số loại huy hiệu để trả lời một câu hỏi gần 5 năm sau khi một câu trả lời vượt trội được cung cấp!


18

Bạn có thể dùng

catch(...)

nhưng điều đó rất nguy hiểm. Trong cuốn sách Gỡ lỗi Windows , John Robbins kể một câu chuyện chiến tranh về một lỗi thực sự khó chịu được che dấu bởi lệnh bắt (...). Bạn tốt hơn nhiều khi nắm bắt các ngoại lệ cụ thể. Nắm bắt bất cứ điều gì bạn nghĩ rằng khối thử của bạn có thể ném hợp lý, nhưng hãy để mã ném ngoại lệ lên cao hơn nếu có điều gì đó thực sự bất ngờ xảy ra.


1
Tôi chỉ bắt được một số cách sử dụng chúng và tiêu trong một số đăng nhập ở giai đoạn đó. Không làm gì với một ngoại lệ chắc chắn là yêu cầu rắc rối.
jxramos

15

Hãy để tôi đề cập đến điều này ở đây: Java

try 
{
...
}
catch (Exception e)
{
...
}

có thể KHÔNG bắt tất cả các ngoại lệ! Tôi thực sự đã có những điều như vậy xảy ra trước đây, và nó gây kích động; Ngoại lệ bắt nguồn từ throwable. Vì vậy, theo nghĩa đen, để nắm bắt mọi thứ, bạn KHÔNG muốn bắt Ngoại lệ; bạn muốn bắt Ném.

Tôi biết điều đó nghe có vẻ khó chịu, nhưng khi bạn mất vài ngày cố gắng tìm ra "ngoại lệ chưa được bắt nguồn" xuất phát từ mã được bao quanh bởi một khối thử ... bắt (Exception e) ", nó dính vào bạn.


2
Tất nhiên, bạn không bao giờ nên bắt các đối tượng Lỗi - nếu bạn phải bắt chúng, chúng sẽ là Ngoại lệ. Các đối tượng lỗi là những thứ hoàn toàn gây tử vong, chẳng hạn như hết dung lượng heap, v.v.
SCdF

1
Không có ngoại lệ thời gian chạy mà hầu hết các lần ngoại lệ GoodProgrammerExpected !!!
OscarRyz

3
Chúng tôi đã có một lỗi thực sự nghiêm trọng do bắt OutOfMemoryError do một khối bắt (Có thể ném) thay vì để nó giết chết mọi thứ ...
Trejkaz

1
Tất nhiên catch(Exception)có thể không bắt được tất cả các ngoại lệ trong Java, bạn đang làm cho nó bị lẫn với C # ... Java = catch(Thowable), C # = catch(Exception). Đừng làm họ bối rối.
Đầu bếp Pharaoh

2
@OscarRyz Nghe có vẻ như CoderMalfunctionError(thực sự là một Errorlớp con Java thực sự ... mặc dù nó không có nghĩa là nó nghe như thế nào.)
reirab

9

Chà, nếu bạn muốn bắt tất cả ngoại lệ để tạo một kết xuất nhỏ chẳng hạn ...

Ai đó đã làm việc trên Windows.

Xem http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus Trong bài viết, ông giải thích cách ông tìm ra cách bắt tất cả các loại ngoại lệ và ông cung cấp mã hoạt động.

Dưới đây là danh sách bạn có thể bắt:

 SEH exception
 terminate
 unexpected
 pure virtual method call
 invalid parameter
 new operator fault 
 SIGABR
 SIGFPE
 SIGILL
 SIGINT
 SIGSEGV
 SIGTERM
 Raised exception
C++ typed exception

Và cách sử dụng: CCrashHandler ch; ch.SetProcessExceptionHandlers (); // làm điều này cho một luồng ch.SetThreadExceptionHandlers (); // cho mỗi thred


Theo mặc định, điều này tạo ra một kết xuất nhỏ trong thư mục hiện tại (crashdump.dmp)


4

Một cơ chế bắt ngoại lệ chung sẽ chứng minh cực kỳ hữu ích.

Nghi ngờ. Bạn đã biết mã của bạn bị hỏng, vì nó bị lỗi. Ăn ngoại lệ có thể che giấu điều này, nhưng điều đó có thể sẽ dẫn đến những con bọ thậm chí còn tinh vi hơn.

Những gì bạn thực sự muốn là một trình gỡ lỗi ...


13
Tôi không đồng ý, có rất nhiều trường hợp trong các ứng dụng thời gian thực mà tôi muốn bắt gặp một ngoại lệ không xác định, viết bất cứ điều gì vào nhật ký / theo đuổi một số hành động lỗi chung chung, thay vì để ứng dụng bị sập.
f0ster

3
Tôi khá nghi ngờ bạn đang nghĩ đến những trường hợp bạn có thể theo đuổi một số hành động lỗi chung chung, thuận tiện bỏ qua những nơi mà ngăn xếp bị vứt bỏ hoặc bộ nhớ bị cạn kiệt và việc xử lý lỗi chung cũng sẽ không thành công. Không có gì sai khi bắt các lỗi mà bạn có thể phục hồi, nhưng IMHO chỉ bắt được tất cả khi bị cô lập (ngăn xếp riêng, bộ nhớ được phân bổ trước), logic được viết cẩn thận được gọi ngay trước khi kết thúc chương trình; nếu bạn không biết vấn đề là gì, bạn không thể tự tin rằng nó có thể được phục hồi.
Shog9

1
Tức là cài đặt một trình xử lý tín hiệu giúp giải phóng một số nhật ký bạn xây dựng trong thời gian chạy để tìm ra nơi chương trình bị hỏng và, hy vọng, tại sao.
Rõ ràng hơn

3
  1. Bạn có thể chạy ứng dụng Java sử dụng JNI của mình từ cửa sổ giao diện điều khiển (khởi chạy nó từ dòng lệnh java) để xem liệu có bất kỳ báo cáo nào về những gì có thể đã được phát hiện trước khi JVM bị hỏng không. Khi chạy trực tiếp dưới dạng ứng dụng cửa sổ Java, bạn có thể thiếu các thông báo sẽ xuất hiện nếu bạn chạy từ cửa sổ giao diện điều khiển thay thế.

  2. Thứ hai, bạn có thể gỡ bỏ triển khai DLL JNI của mình để cho thấy các phương thức trong DLL của bạn đang được nhập từ JNI, bạn đang quay lại đúng cách, v.v.?

  3. Chỉ trong trường hợp sự cố xảy ra với việc sử dụng sai một trong các phương thức giao diện JNI từ mã C ++, bạn đã xác minh rằng một số ví dụ JNI đơn giản biên dịch và làm việc với thiết lập của bạn chưa? Tôi đang suy nghĩ cụ thể về việc sử dụng các phương thức giao diện JNI để chuyển đổi các tham số sang các định dạng C ++ gốc và biến các kết quả hàm thành các loại Java. Sẽ rất hữu ích khi bỏ những thứ đó để đảm bảo rằng các chuyển đổi dữ liệu đang hoạt động và bạn sẽ không gặp rắc rối trong các cuộc gọi giống như COM vào giao diện JNI.

  4. Có nhiều thứ khác để kiểm tra, nhưng thật khó để đề xuất bất kỳ điều gì mà không biết thêm về các phương thức Java gốc của bạn là gì và việc triển khai JNI của chúng đang cố gắng làm gì. Không rõ ràng rằng việc bắt một ngoại lệ từ cấp mã C ++ có liên quan đến vấn đề của bạn. (Bạn có thể sử dụng giao diện JNI để lấy lại ngoại lệ dưới dạng Java, nhưng không rõ ràng về những gì bạn cung cấp rằng điều này sẽ giúp ích.)


2

Đối với vấn đề thực sự về việc không thể gỡ lỗi chính xác một chương trình sử dụng JNI (hoặc lỗi không xuất hiện khi chạy chương trình gỡ lỗi):

Trong trường hợp này, nó thường giúp thêm các trình bao bọc Java xung quanh các cuộc gọi JNI của bạn (tức là tất cả các phương thức gốc là riêng tư và các phương thức công khai của bạn trong lớp gọi chúng) thực hiện một số kiểm tra vệ sinh cơ bản (kiểm tra xem tất cả "đối tượng" có được giải phóng và "đối tượng" không không được sử dụng sau khi giải phóng) hoặc đồng bộ hóa (chỉ đồng bộ hóa tất cả các phương thức từ một DLL sang một đối tượng duy nhất). Hãy để các phương thức trình bao bọc java ghi lại lỗi và đưa ra một ngoại lệ.

Điều này thường sẽ giúp tìm ra lỗi thực sự (điều đáng ngạc nhiên là phần lớn trong mã Java không tuân theo ngữ nghĩa của các hàm được gọi gây ra một số giải phóng kép khó chịu hoặc tương tự) dễ dàng hơn là cố gắng gỡ lỗi một chương trình Java song song ồ ạt trong một trình gỡ lỗi bản địa ...

Nếu bạn biết nguyên nhân, hãy giữ mã trong các phương thức trình bao bọc của bạn để tránh nó. Tốt hơn là các phương thức trình bao bọc của bạn đưa ra các ngoại lệ so với mã JNI của bạn làm sập VM ...


1

Vâng, điều này thực sự phụ thuộc vào môi trường trình biên dịch. gcc không bắt được chúng. Visual Studio và Borland cuối cùng mà tôi đã sử dụng.

Vì vậy, kết luận về sự cố là nó phụ thuộc vào chất lượng môi trường phát triển của bạn.

Đặc tả C ++ nói rằng bắt (...) phải bắt bất kỳ trường hợp ngoại lệ nào, nhưng nó không trong mọi trường hợp.

Ít nhất là từ những gì tôi đã cố gắng.


0

Hãy nhận biết

try{
// ...
} catch (...) {
// ...
}

chỉ bắt các ngoại lệ ở cấp độ ngôn ngữ, các ngoại lệ / lỗi cấp thấp khác như Access ViolationSegmentation Faultsẽ không bị bắt.


Những thứ như Phân đoạn lỗi không thực sự là ngoại lệ, chúng là tín hiệu; do đó, bạn không thể bắt chúng như ngoại lệ điển hình. Tuy nhiên, có một số cách giải quyết như thế này .
MAChitgarha
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.