2009-07-27 32 views
10

Tôi biết rằng tôi ném ngoại lệ từ một destructor.Destructor gọi một chức năng có thể ném ngoại lệ trong C++

Nếu destructor của tôi gọi một chức năng có thể ném một ngoại lệ, là nó OK nếu tôi bắt nó trong destructor và không ném nó hơn nữa? Hoặc nó có thể gây ra hủy bỏ anyway và tôi không nên gọi các chức năng như vậy từ một destructor ở tất cả?

+0

Chỉ để làm rõ, bạn có hỏi liệu nó có ổn không nếu destructor bắt được ngoại lệ, vì vậy nó không bao giờ rời khỏi destructor, hoặc nếu nó là ok để cho nó rời khỏi destructor miễn là nó bị bắt bên ngoài? – jalf

+0

Tôi đang hỏi liệu có OK không nếu ngoại lệ nằm trong D'tor. –

Trả lời

19

Vâng, đó là hợp pháp. Một ngoại lệ không được thoát khỏi từ trình phá hủy, nhưng bất kỳ điều gì xảy ra bên trong trình phá hủy hoặc trong các chức năng mà cuộc gọi đó, tùy thuộc vào bạn.

(Về mặt kỹ thuật, một ngoại lệ có thể thoát khỏi một cuộc gọi destructor là tốt. Nếu điều đó xảy ra trong đống thư giãn vì một ngoại lệ được ném, std::terminate được gọi. Vì vậy, nó được xác định rõ theo tiêu chuẩn, nhưng đó là một thực sự ý tưởng tồi.)

+2

Xin lỗi vì đã viết nitpicking, nhưng tôi sẽ sử dụng một thuật ngữ khác "hợp pháp". Ném một ngoại lệ trong destructor cũng là "hợp pháp", i. e. nó sẽ biên dịch và chạy. Nhưng đó là một thực tế xấu sẽ gây ra những hiệu ứng khó chịu. – Dima

+0

Có và không. Bạn nói đúng đó là pháp lý về mặt kỹ thuật để ném một ngoại lệ từ một destructor ('std :: terminate' được gọi là sau đó). Nhưng definitoin của bạn "hợp pháp" là sai. Chỉ vì một cái gì đó biên dịch và chạy không có cách nào làm cho nó hợp pháp C + +. – jalf

+1

Tôi không chắc chắn bạn muốn có ý nghĩa gì về mặt pháp lý, nhưng tôi với Dima về vấn đề này.Mặc dù C++ không sử dụng công việc hợp pháp, nhưng nó định nghĩa một chương trình * được định dạng tốt (có lẽ là gần nhất với những gì tôi nghĩ là 'hợp pháp'), * hành vi không xác định * và * hành vi không xác định *. Việc cho phép một ngoại lệ đẩy ra khỏi một destructor có thể xảy ra trong một chương trình được hình thành tốt và hành vi không phải là không xác định, cũng như không xác định. Điều đó không ngăn nó trở thành một hành vi gần như không phổ biến. –

-1

Bạn có thể tìm thấy this page từ C++ FAQ Lite để có nhiều thông tin. Câu trả lời cơ bản là, "không làm điều đó." Trường hợp chính xác bạn có kế hoạch để bắt ngoại lệ này? Dù bạn dự định làm gì khi bạn bắt ngoại lệ đó, chỉ cần thực hiện thay thế bằng một cuộc gọi hàm hoặc một cái gì đó (ví dụ: đăng nhập hoặc đặt cờ để cảnh báo người dùng hoặc bất kỳ điều gì). Việc ném ngoại lệ từ trình phá hủy sẽ gây nguy cơ khiến toàn bộ chương trình của bạn chấm dứt.

+0

Điều này liên quan đến câu hỏi như thế nào? Nếu anh ta gọi một hàm có thể ném std :: bad_alloc, cái gì sẽ là lựa chọn thay thế? Viết một hàm "wrapper"? Đó không phải là một giải pháp, mà chỉ ẩn ngoại lệ trong một lớp khác. – MSalters

0

Câu trả lời đơn giản, không bao giờ cho phép ngoại lệ từ một dtor!

Câu trả lời phức tạp. Bạn chỉ được thực sự đóng đinh nếu ngoại lệ thoát khỏi dtor trong khi một ngoại lệ khác đang hoạt động. Trường hợp bình thường cho điều này là khi bạn đã mở ngăn xếp từ một ngoại lệ khác và đối tượng được đề cập bị hủy. Trong trường hợp đó, nếu ngoại lệ thoát khỏi dtor thì std::terminate được gọi, lưu ý rằng bạn có thể đặt trong trình xử lý của riêng bạn cho std::terminate bằng cách gọi std::set_terminate. Việc triển khai mặc định là std::terminate là để hủy cuộc gọi.

Để những điều phức tạp hơn, hầu hết các chức năng mà muốn thực hiện bất kỳ sự bảo đảm về an toàn ngoại lệ của họ, chủ yếu là đảm bảo cơ bản hoặc giấy cam kết mạnh mẽ, dựa trên các loại cơ bản để bản thân không ném vào dtor của họ *

Các thực câu hỏi là, chương trình của bạn sẽ ở trạng thái nào khi lỗi này xảy ra? Làm thế nào bạn có thể phục hồi? Việc phục hồi này sẽ được xử lý ở đâu? Bạn cần phải xem xét trường hợp cụ thể của bạn và làm việc những vấn đề này ra ngoài. Đôi khi nó là tốt để bắt ngoại lệ và bỏ qua nó. Lần khác bạn cần phải nâng cao một số lá cờ đỏ.

Vì vậy, câu trả lời là: nó cho phép bởi C++ để ném một ngoại lệ trong một dtor, nhưng bạn không bao giờ nên cho phép nó trốn thoát.

* Dưới đây là một tóm tắt synopsis của người nhận bảo lãnh ngoại lệ (đây là một lâu hơn nữa article)

  1. Tóm tắt: Tóm lại xác định đảm bảo Abrahams an toàn ngoại lệ (cơ bản, mạnh mẽ, và nothrow).

Việc bảo lãnh cơ bản là thất bại hoạt động có thể thay đổi trạng thái chương trình, nhưng không có rò rỉ xảy ra và ảnh hưởng đối tượng/modules vẫn còn phá hủy và có thể sử dụng, trong một phù hợp (nhưng không nhất thiết phải dự đoán được ) nhà nước.

Việc bảo lãnh mạnh mẽ liên quan đến giao dịch cam/rollback ngữ nghĩa: thất bại trong hoạt động đảm bảo chương trình nhà nước là không thay đổi với đối với các đối tượng hoạt động trên. Điều này có nghĩa là không có tác dụng phụ nào ảnh hưởng đến đối tượng, kể cả giá trị hoặc nội dung của các đối tượng trợ giúp có liên quan chẳng hạn như các trình vòng lặp trỏ vào các vùng chứa đang được thao tác.

Đảm bảo không đảm bảo nghĩa là các thao tác không thành công sẽ không xảy ra. Các hoạt động sẽ không ném một ngoại lệ.

+0

khi tôi hiểu câu hỏi, anh ta không hỏi về ngoại lệ rời bỏ dtor, nhưng đơn giản là cho phép dtor gọi một hàm mà ném một ngoại lệ, miễn là dtor bắt được ngoại lệ để nó không lan truyền – jalf

3

Có.

Xem std :: fstream class trong thư viện chuẩn để biết ví dụ.

  • close() có khả năng có thể ném ngoại lệ.
  • Máy hủy có thể gọi gần() nhưng bộ hủy không ném (sẽ nuốt bất kỳ ngoại lệ nào).

Khái niệm là nếu destructor gọi bất kỳ phương thức nào có thể ném thì các phương thức này sẽ được công khai. Do đó, nếu người dùng đối tượng của bạn muốn kiểm tra các ngoại lệ, họ có thể sử dụng các phương thức công khai và xử lý ngoại lệ. Nếu họ không quan tâm đến ngoại lệ thì chỉ để cho destructor xử lý vấn đề.

Quay lại ví dụ tiêu chuẩn :: fstream.

{ 
    std::fstream text("Plop"); 
    // Load Text. 

    // I don't care if the close fails. 
    // So let the destructor handle it and discard exceptions 
} 



{ 
    // If this fails to write I should at least warn the user. 
    // So in this case I will explicitly try and close it. 
    try 
    { 
     std::ofstram password("/etc/password"); 
     // Update the password file. 

     password.close(); 
    } 
    catch(...) 
    { 
      Message.ShowDialog("You failed to update the Password File"); 
    } 
} 
1

Bạn có thể tìm thấy một số ví dụ ở đây: https://software.intel.com/sites/products/documentation/doclib/iss/2013/sa-ptr/sa-ptr_win_lin/GUID-D2983B74-74E9-4868-90E0-D65A80F8F69F.htm

Nếu một ngoại lệ lá destructor trong đống cuộn ngoại lệ khác được tuyên truyền, sau đó std :: chấm dứt() được gọi.

Khi không có thư giãn đang trong quá trình xử lý, ngoại lệ có thể thoát khỏi hàm hủy mà không có lệnh std :: terminate() được gọi. Tuy nhiên, đối với các đối tượng được phân bổ trên heap điều này sẽ dẫn đến rò rỉ bộ nhớ vì "toán tử xóa" sẽ không được gọi cho đối tượng người ném ngoại lệ ra khỏi destructor của nó. Đáng ngạc nhiên, phá hủy của lớp cơ sở vẫn được gọi trong trường hợp này: What happens to base class destructor if a derived class destructor throws an exception

Nếu ngoại lệ được bắt bên trong destructor (để ngoại lệ không rời khỏi destructor), thì không có vấn đề gì ngay cả khi chồng tháo biệt của một ngoại lệ khác trong tiến trình. Trường hợp này được mô tả sâu hơn ở đây: http://bin-login.name/ftp/pub/docs/programming_languages/cpp/cffective_cpp/MEC/MI11_FR.HTM

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