hủy bỏ, chấm dứt hoặc thoát?


112

Sự khác biệt giữa ba cái đó là gì và tôi sẽ kết thúc chương trình như thế nào trong trường hợp ngoại lệ mà tôi không thể xử lý đúng cách?


3
Đây không phải là một bản sao, mà là một tập hợp con với một số câu trả lời hay stackoverflow.com/questions/397075/… và nó cũng được gắn thẻ C ++!
Ellie Kesselman

std::abortlà hợp lý nếu một ngoại lệ không thể được giải quyết trong một trình hủy.
Daniel

1
Để biết thêm thông tin, std::terminatehãy xem các bài viết này trong blog C ++ xuất sắc của Andrzej: akrzemi1.wordpress.com/2011/09/28/who-calls-stdtermina , akrzemi1.wordpress.com/2011/10/05/using-stdtermina
Ohad Schneider

Câu trả lời:


3

Lời khuyên của tôi là không sử dụng bất kỳ cái nào trong số chúng. Thay vào đó, catchcác trường hợp ngoại lệ bạn không thể xử lý main()và đơn giản là returntừ đó. Điều này có nghĩa là bạn được đảm bảo rằng việc giải nén ngăn xếp diễn ra chính xác và tất cả các trình hủy được gọi. Nói cách khác:

int main() {
    try {
       // your stuff
    }
    catch( ... ) {
       return 1;    // or whatever
    }
}

8
@Neil: Về cơ bản đã đồng ý, nhưng các trường hợp ngoại lệ mà chương trình không thể xử lý nên được báo cáo và phát triển lại. Để ứng dụng gặp sự cố.
John Dibling

13
Để đảm bảo ngăn xếp mở ra, bạn phải luôn bắt trong chính. Nhưng tôi sẽ ném lại từ bắt. Vì một số hệ điều hành có khả năng tự động gọi cơ sở hạ tầng gỡ lỗi nếu bạn đã biên dịch trong gỡ lỗi.
Martin York

5
Các trường hợp ngoại lệ mà trình xử lý cấp cao nhất không bắt được có thể gọi một phương tiện báo cáo hệ thống kết xuất quá trình và tải lên báo cáo ngoại lệ để các nhà phát triển chú ý, như Báo cáo lỗi Windows, báo cáo lỗi Mac OS X và nhật ký lỗi ứng dụng iPhone.
JBRWilkinson

6
@John Lý do khiến tôi ngạc nhiên là, mặc dù nó có ý nghĩa hoàn hảo về cách các ngoại lệ thực sự được triển khai, nhưng nó phá vỡ sự trừu tượng rằng một ngoại lệ "truyền lên ngăn xếp" cho đến khi tìm thấy một trình xử lý phù hợp (hoặc kết thúc là gọi là). Và những điều trừu tượng bị rò rỉ, trong khi thường không thể tránh khỏi, nhất thiết phải gây ngạc nhiên khi gặp phải.
Tyler McHenry

11
-1 vì điều này không trả lời một nửa câu hỏi. "Sự khác biệt giữa [hủy bỏ, chấm dứt hoặc thoát là gì?]" Đây là câu trả lời hay hơn: stackoverflow.com/a/397081/353094 Ngoài ra stackoverflow.com/a/2820407/353094 là một câu trả lời tuyệt vời.
leetNightshade

149
  • abort cho biết kết thúc "bất thường" của chương trình và tăng tín hiệu POSIX SIGABRT, có nghĩa là bất kỳ trình xử lý nào mà bạn đã đăng ký cho tín hiệu đó sẽ được gọi, mặc dù chương trình vẫn sẽ kết thúc từ sau trong cả hai trường hợp. Thông thường, bạn sẽ sử dụng aborttrong chương trình C để thoát khỏi trường hợp lỗi không mong muốn, trong đó lỗi có thể là lỗi trong chương trình, chứ không phải là một cái gì đó như đầu vào kém hoặc lỗi mạng. Ví dụ, bạn có thể abortnếu một cấu trúc dữ liệu được tìm thấy có con trỏ NULL trong đó khi điều đó về mặt logic sẽ không bao giờ xảy ra.

  • exit cho biết kết thúc "bình thường" đối với chương trình, mặc dù điều này vẫn có thể cho thấy lỗi (nhưng không phải lỗi). Nói cách khác, bạn có thể exitgặp mã lỗi nếu người dùng nhập thông tin đầu vào không thể phân tích cú pháp hoặc không thể đọc tệp. Mã thoát 0 cho biết thành công. exitcũng tùy chọn gọi các trình xử lý trước khi nó kết thúc chương trình. Chúng được đăng ký với các chức năng atexiton_exit.

  • std :: end là những gì được gọi tự động trong chương trình C ++ khi có một ngoại lệ chưa được xử lý. Về cơ bản, điều này tương đương với C ++ abort, giả sử rằng bạn đang báo cáo tất cả các lỗi đặc biệt của mình bằng cách ném các ngoại lệ. Điều này gọi một trình xử lý được đặt bởi std::set_terminatehàm, theo mặc định, hàm này chỉ đơn giản là gọi abort.

Trong C ++, bạn thường muốn tránh gọi aborthoặc exitgặp lỗi, vì tốt hơn hết bạn nên ném một ngoại lệ và để mã cao hơn trong ngăn xếp cuộc gọi quyết định việc kết thúc chương trình có phù hợp hay không. Việc bạn sử dụng exitđể thành công hay không là một vấn đề hoàn cảnh - việc kết thúc chương trình ở một nơi nào đó khác với câu lệnh return trong có hợp lý hay không main.

std::terminatenên được coi là một công cụ báo cáo lỗi cuối cùng, ngay cả trong C ++. Vấn đề std::terminatelà trình xử lý kết thúc không có quyền truy cập vào ngoại lệ đã được xử lý, vì vậy không có cách nào để biết nó là gì. Bạn thường tốt hơn nhiều khi gói toàn bộ main trong một try { } catch (std::exception& ex) { }khối. Ít nhất thì bạn có thể báo cáo thêm thông tin về các ngoại lệ bắt nguồn từ std::exception(mặc dù tất nhiên các ngoại lệ không bắt nguồn từ std::exceptionđó vẫn sẽ được xử lý).

Việc bao bọc phần thân maintrong try { } catch(...) { }không tốt hơn nhiều so với việc đặt một trình xử lý kết thúc, vì một lần nữa bạn không có quyền truy cập vào ngoại lệ được đề cập. Chỉnh sửa: Theo câu trả lời của Neil Butterworth, có một lợi ích là ngăn xếp không bị ràng buộc trong trường hợp này, điều này (hơi ngạc nhiên) không đúng với một ngoại lệ không được xử lý.


10
Bạn có thể cập nhật câu trả lời này với thông tin C ++ 11 không? Có vẻ như bây giờ có nhiều cách để lấy ngoại lệ trong hàm catch (...) và trong trình xử lý kết thúc.
Klaim

1
Trong C ++, trình xử lý kết thúc không có quyền truy cập vào ngoại lệ thông qua std::current_exception(). Xem ví dụ ở đây: akrzemi1.wordpress.com/2011/10/05/using-stdterminate
anorm

Không quan trọng là bạn có thể nhận được ngoại lệ hiện tại, vì bạn không thể kiểm tra nó. Tất cả những gì bạn có thể làm là ném lại nó.
seattlecpp

2
@seattlecpp bạn có thể ném lại nó và bắt tham chiếu đến nó, sau đó bạn có thể kiểm tra
gpeche

16

std :: abort và std :: exit (và hơn thế nữa: std :: _ Exit, std :: quick_exit) chỉ là các hàm cấp thấp hơn. Bạn sử dụng chúng để cho chương trình biết chính xác những gì bạn muốn nó làm: hàm hủy (và nếu) sẽ gọi, hàm dọn dẹp nào khác sẽ gọi, giá trị trả về, v.v.

std :: chấm dứt là một trừu tượng cấp cao hơn: nó được gọi (theo thời gian chạy hoặc bạn) để chỉ ra rằng một lỗi trong chương trình đã xảy ra và vì lý do nào đó mà không thể xử lý bằng cách ném một ngoại lệ. Sự cần thiết cho điều đó thường xảy ra khi lỗi xảy ra trong chính cơ chế ngoại lệ, nhưng bạn có thể sử dụng nó bất kỳ lúc nào khi bạn không muốn chương trình của mình tiếp tục vượt quá lỗi đã cho. Tôi đã biên soạn danh sách đầy đủ các tình huống khi std :: end được gọi trong bài đăng của mình. Nó không được chỉ định std :: Term làm gì, vì bạn đang kiểm soát nó. Bạn có thể cấu hình hành vi bằng cách đăng ký bất kỳ chức năng nào. Các hạn chế của bạn là chức năng không thể quay trở lại trang web lỗi và nó không thể thoát ra ngoài thông qua một ngoại lệ, nhưng về mặt kỹ thuật, bạn thậm chí có thể bắt đầu bơm thông báo của mình vào bên trong. Để biết danh sách những điều hữu ích mà bạn có thể làm bên trong, hãy xem bài đăng khác của tôi .

Đặc biệt, lưu ý rằng std :: chấm dứt được coi là một trình xử lý ngoại lệ trong các ngữ cảnh mà std :: chấm dứt được gọi do một ngoại lệ được ném ra không thể xử lý được và bạn có thể kiểm tra ngoại lệ đó là gì và kiểm tra nó bằng cách sử dụng C ++ 11 sử dụng std :: rethrow_exception và std :: current_exception. Đó là tất cả trong bài viết của tôi .


Có nên có một trình xử lý dọn dẹp trong trường hợp chương trình kết thúc do tín hiệu hệ thống không? Ví dụ, truy cập bộ nhớ không hợp lệ dẫn đến việc tạo ra tín hiệu SIGSEGV. Trong trường hợp này, có tốt không nếu chỉ để chương trình kết thúc và có tệp lõi hoặc đăng ký một trình xử lý tín hiệu để thực hiện dọn dẹp ?? Có bất kỳ mối lo ngại nào về việc thực hiện dọn dẹp trong khi xử lý các tín hiệu hệ thống so với việc thực hiện dọn dẹp trong khi xử lý std :: end không?
kartik trivikram

12

quick_exit () !

Nếu chương trình của bạn là đa luồng, thì việc gọi exit()rất có thể sẽ dẫn đến sự cố vì các std::threadđối tượng toàn cục / tĩnh sẽ được cố gắng hủy mà không thoát khỏi luồng của chúng.

Nếu bạn muốn trả về mã lỗi và thoát khỏi chương trình (nhiều hơn hoặc ít hơn) bình thường, hãy gọi quick_exit()các chương trình đa luồng. Đối với kết thúc bất thường (không có khả năng bạn chỉ định mã lỗi), abort()hoặc std::terminate()có thể được gọi.

Lưu ý: quick_exit () chưa được MSVC ++ hỗ trợ cho đến phiên bản 2015.


4
  • chấm dứt để lại cho bạn khả năng đăng ký những gì sẽ xảy ra khi nó được gọi. Nên là một trong hai cái còn lại.
  • exit là một lối ra bình thường cho phép chỉ định một trạng thái thoát. Các trình xử lý được đăng ký bởi at_exit () được chạy
  • bỏ thai là một lối thoát bất thường. Thứ duy nhất được chạy là trình xử lý tín hiệu cho SIGABRT.

4
  • end () được gọi tự động khi một ngoại lệ xảy ra mà không thể xử lý được. Theo mặc định, các lệnh gọi end () abort (). Bạn có thể đặt một chốt điều khiển tùy chỉnh bằng hàm set_termina ().

    abort () gửi tín hiệu SIGABRT.

    exit () không nhất thiết là một điều xấu. Nó thoát ứng dụng thành công và gọi các hàm atexit () theo thứ tự LIFO. Tôi thường không thấy điều này trong các ứng dụng C ++, tuy nhiên, tôi thấy nó trong nhiều ứng dụng dựa trên unix nơi nó gửi mã thoát ở cuối. Thông thường, lối ra (0) cho biết ứng dụng đã chạy thành công.


8
Không thành công! Trong cả Unix và DOS, exit (0) cho biết thành công và bất kỳ giá trị nào khác được chuyển đến exit () cho biết thất bại, không phải ngược lại!
Richard Barrell
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.