ném mới std :: ngoại lệ so với ném std :: ngoại lệ


113

trong khi xem xét một số mã, tôi tình cờ gặp phải:

throw /*-->*/new std::exception ("//...

và tôi luôn nghĩ rằng bạn không cần / bạn không nên sử dụng newở đây.
Cách chính xác là gì, cả hai đều OK, nếu vậy thì có sự khác biệt nào không?

BTW từ những gì tôi có thể nhìn thấy trong khi "gửi" với PowerShell tăng libs không bao giờ sử dụng throw new.

PS cũng tôi tìm thấy một số mã CLI sử dụng throw gcnew. Ổn chứ?


1
Tôi nghĩ throw gcnewsẽ hữu ích, ví dụ. nếu bạn muốn mã được quản lý để bắt ngoại lệ của bạn. Ai đó có thể sửa cho tôi về điều đó?
jpalecek

1
.Net xử lý các ngoại lệ bằng con trỏ, vì vậy, ném gcnew là điều đúng đắn để làm ở đó.
Sebastian Redl

1
@SebastianRedl .Net "con trỏ" có thể không rõ ràng? Mặc dù gcnew chắc chắn là không. System::Exceptionnói chung là một tham chiếu đến một đối tượng được quản lý trên đống rác được thu thập. Tôi luôn ném gcnewvà bắt với System::Exception ^. Tất nhiên tôi cũng sử dụng finallyC ++ / CLI mọi lúc, mặc dù không thường xuyên kết hợp với các ngoại lệ C ++ trong cùng một trykhối, tôi không chắc tại sao.

Câu trả lời:


89

Cách thông thường để ném và bắt các ngoại lệ là ném một đối tượng ngoại lệ và bắt nó bằng tham chiếu (thường là consttham chiếu). Ngôn ngữ C ++ yêu cầu trình biên dịch tạo ra mã thích hợp để xây dựng đối tượng ngoại lệ và dọn dẹp nó đúng cách vào thời điểm thích hợp.

Ném một con trỏ đến một đối tượng được cấp phát động không bao giờ là một ý kiến ​​hay. Các ngoại lệ được cho là cho phép bạn viết mã mạnh mẽ hơn khi đối mặt với các điều kiện lỗi. Nếu bạn ném một đối tượng ngoại lệ theo cách thông thường, bạn có thể chắc chắn rằng liệu nó có bị bắt bởi mệnh đề catch đặt tên đúng kiểu hay không, bởi a catch (...), liệu nó có được ném lại hay không, nó sẽ bị hủy đúng vào thời điểm thích hợp. (Trường hợp ngoại lệ duy nhất nếu nó không bao giờ bị bắt nhưng đây là một tình huống không thể khôi phục được cho dù bạn nhìn nó theo cách nào.)

Nếu bạn ném một con trỏ đến một đối tượng được cấp phát động, bạn phải chắc chắn rằng bất kỳ thứ gì ngăn xếp cuộc gọi trông như thế nào tại điểm bạn muốn ném ngoại lệ của mình, đều có một khối bắt tên đúng loại con trỏ và có lệnh deletegọi thích hợp . Ngoại lệ của bạn không bao giờ được bắt catch (...)trừ khi khối đó ném lại ngoại lệ mà sau đó bị chặn bởi một khối bắt khác xử lý đúng với ngoại lệ.

Một cách hiệu quả, điều này có nghĩa là bạn đã sử dụng tính năng xử lý ngoại lệ giúp viết mã mạnh mẽ dễ dàng hơn và rất khó để viết mã đúng trong mọi tình huống. Điều này loại bỏ vấn đề rằng hầu như không thể hoạt động như mã thư viện cho mã khách hàng sẽ không mong đợi tính năng này.


1
"ném một đối tượng ngoại lệ" ngăn xếp hoặc đống bạn bè của tôi? Stack hay heap? (Có thể tôi đã xem xét một ví dụ toàn cầu xấu ở đâu đó) ồ và nếu ngăn xếp, thì phạm vi thích hợp là gì?

@ebyrob: Tôi thực sự không chắc bạn đang hỏi về điều gì nhưng có vẻ như bạn muốn biết về dung lượng lưu trữ và / hoặc thời gian tồn tại của đối tượng ngoại lệ, điều này có thể được trả lời ở đây . Nếu không, bạn có thể nên hỏi một câu hỏi riêng.
CB Bailey

31

Không cần sử dụng newkhi ném ngoại lệ.

Chỉ viết:

throw yourexception(yourmessage);

và bắt như:

catch(yourexception const & e)
{
      //your code (probably logging related code)
}

Lưu ý rằng yourexceptionnên xuất phát từ std::exceptiontrực tiếp hoặc gián tiếp.


7
Tại sao? tại sao không sử dụng new? tại sao bắt nguồn yourexceptiontừ std::exception?
Walter

Khi tôi lười biếng (thường xuyên) tại sao lại không throw std::exception;hoạt động? g ++ sẽ không có vẻ để biên dịch nó ...

7
@ebyrob: std::exceptionlà một kiểu, và bạn không thể ném một kiểu , bạn phải ném một đối tượng . Vì vậy, cú pháp sẽ là: throw std::exception();Điều đó sẽ biên dịch. Bây giờ điều đó tốt như thế nào, hoàn toàn là một câu hỏi khác.
Nawaz

22

Việc ném new std::exceptionlà chính xác nếu trang web cuộc gọi đang mong đợi bắt được a std::exception*. Nhưng sẽ không ai mong đợi để bắt được một con trỏ đến một ngoại lệ. Ngay cả khi bạn ghi lại đó là những gì chức năng của bạn thực hiện và mọi người đọc tài liệu, họ vẫn có thể quên và cố gắng bắt một tham chiếu đến một std::exceptionđối tượng.


27
Việc ném new std::exceptionchỉ đúng nếu trang web gọi đang mong đợi để bắt một con trỏ VÀ đang mong đợi tiếp quản việc quản lý ngoại lệ cấp phát VÀ sẽ không bao giờ có bất kỳ trường hợp nào mà hàm của bạn sẽ được gọi bởi một cái gì đó không bắt rõ ràng con trỏ chính xác ( catch(...)hoặc không xử lý gì cả) nếu không sẽ có một đối tượng bị rò rỉ. Trong ngắn hạn, điều này có thể được coi là "không bao giờ".
CB Bailey

Thật tò mò làm thế nào câu trả lời này được chấp nhận, trong khi thực sự nhận xét của @ CharlesBailey là câu trả lời chính xác.
John Dibling

@John: Điều đó cũng lướt qua tâm trí tôi. Nhưng tôi nghĩ cú đấm có một không hai có tác dụng tốt khi tôi đưa ra bản tóm tắt khô khan và Charles mở rộng một cách thú vị về nhiều cách khác nhau mà mọi người có thể quên để giải quyết nó một cách hợp lý. Thật tệ là bạn không nhận được danh tiếng từ những bình luận được bình chọn cao.

Charles đã không đưa ra câu trả lời của mình, và câu A này (không giống như câu kia) có những lời giải thích ở cả phần A và bình luận.
NoSenseEtAl

9

Câu hỏi thường gặp về C ++ có một cuộc thảo luận thú vị về điều này:

  1. https://isocpp.org/wiki/faq/exceptions#what-to-catch
  2. https://isocpp.org/wiki/faq/exceptions#catch-by-ptr-in-mfc

Về cơ bản "trừ khi có lý do chính đáng để không, hãy bắt theo giá trị. Tránh bắt theo giá trị, vì điều đó khiến một bản sao được tạo ra và bản sao có thể có hành vi khác với những gì đã được ném. Chỉ trong những trường hợp rất đặc biệt, bạn mới nên bắt bằng con trỏ. "


2
Như thường lệ, Câu hỏi thường gặp bị nói xấu. Bạn có thể nắm bắt theo giá trị hoặc tham chiếu. Một con trỏ chỉ xảy ra là một giá trị (mà bạn nắm bắt theo giá trị hoặc tham chiếu). Hãy nhớ rằng loại Akhác với loại A*vì vậy nếu tôi làm vậy, throw A()tôi KHÔNG thể bắt được catch(A* e)vì nó là loại hoàn toàn khác.
Martin York

Các liên kết này hiện đã bị phá vỡ.
stephenspann

1
Tôi cố định các liên kết @spanndemic
user1202136

1

Nhà khai thác mới không thể đảm bảo rằng nó sẽ không bao giờ đưa ra một ngoại lệ. Vì lý do này, việc sử dụng nó để ném một ngoại lệ "hợp lệ" (dự định) sẽ tạo ra một mã không thể đảm bảo không bị lỗi. Vì có thể chỉ có một ngoại lệ tại một thời điểm và chương trình của bạn cố gắng ném hai ngoại lệ trước khi bất kỳ ngoại lệ nào trong số chúng có thể bị bắt, điều tốt nhất mà một triển khai có thể làm là hủy bỏ ngay chương trình của bạn, ví dụ bằng cách gọi std :: end.

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.