c ++ ngoại lệ: ném std :: string


80

Tôi muốn đưa ra một ngoại lệ khi các phương thức C ++ của tôi gặp phải điều gì đó kỳ lạ và không thể khôi phục. Ném một std::stringcon trỏ có được không?

Đây là những gì tôi mong muốn làm:

void Foo::Bar() {
    if(!QueryPerformanceTimer(&m_baz)) {
        throw new std::string("it's the end of the world!");
    }
}

void Foo::Caller() {
    try {
        this->Bar(); // should throw
    }
    catch(std::string *caught) { // not quite sure the syntax is OK here...
        std::cout << "Got " << caught << std::endl;
    }
}

23
Nó sẽ là hợp pháp, nhưng không phải là đạo đức.
Marcin

18
Bạn bị rò rỉ bộ nhớ. Ai đang xóa con trỏ chuỗi được ném ra? Không sử dụng con trỏ cho các ngoại lệ.
fnieto - Fernando Nieto

2
Tôi biết đó là một hơi muộn, nhưng dù sao, bài viết này có một số điểm về vấn đề này boost.org/community/error_handling.html
Alex Kreimer

Câu trả lời:


100

Đúng. std::exceptionlà lớp ngoại lệ cơ sở trong thư viện chuẩn C ++. Bạn có thể muốn tránh sử dụng các chuỗi làm lớp ngoại lệ vì bản thân chúng có thể tạo ra một ngoại lệ trong quá trình sử dụng. Nếu điều đó xảy ra, thì bạn sẽ ở đâu?

boost có một tài liệu tuyệt vời về phong cách tốt cho các ngoại lệ và xử lý lỗi. Nó đáng để đọc.


20
(! Và nó không phải là khá): mặt lưu ý std :: chấm dứt sẽ được gọi nếu đối tượng ngoại lệ riêng của mình ném, đó là nơi mà bạn sẽ có
Alaric

6
Hãy xem gotw.ca/publications/mill16.htm để biết một lập luận về lý do tại sao việc bận tâm đến việc phân bổ và ném các ngoại lệ là một sự lãng phí thời gian. Một lập luận khác cho câu trả lời này là std :: runtime_exception và gia đình làm điều đó, vậy tại sao bạn không?
Greg Rogers

63

Một số nguyên tắc:

  1. bạn có một lớp cơ sở std :: exception, bạn nên có các ngoại lệ của bạn bắt nguồn từ nó. Bằng cách đó, trình xử lý ngoại lệ chung vẫn có một số thông tin.

  2. Đừng ném con trỏ nhưng đối tượng, theo cách đó bộ nhớ được xử lý cho bạn.

Thí dụ:

struct MyException : public std::exception
{
   std::string s;
   MyException(std::string ss) : s(ss) {}
   ~MyException() throw () {} // Updated
   const char* what() const throw() { return s.c_str(); }
};

Và sau đó sử dụng nó trong mã của bạn:

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw MyException("it's the end of the world!");
  }
}

void Foo::Caller(){
  try{
    this->Bar();// should throw
  }catch(MyException& caught){
    std::cout<<"Got "<<caught.what()<<std::endl;
  }
}

5
Sẽ không tốt hơn nếu bắt nguồn từ std :: runtime_exception?
Martin York

Lưu ý rằng đối số của christopher_f vẫn hợp lệ: Ngoại lệ của bạn có thể tạo ra một ngoại lệ trong quá trình xây dựng ... Thức ăn cho sự suy nghĩ, tôi đoán ... tài liệu tham khảo số?
paercebal 25/09/08

Đối với tham chiếu const, có thể, nhưng không bắt buộc. Tôi đã tự hỏi một lúc về nó ... không tìm thấy bất kỳ tài liệu tham khảo nào cho hay chống lại nó.
PierreBdR

const ref ở đây chỉ hữu ích để bạn không vô tình sửa đổi ngoại lệ trong khối bắt. mà bạn sẽ không làm anyway vì vậy chỉ cần nắm bắt bởi ref nonconst

Tôi đã thử mã này, đã có một lỗi biên dịch, một cái gì đó về phương pháp destructor ...
dividebyzero

24

Tất cả những công việc này:

#include <iostream>
using namespace std;

//Good, because manual memory management isn't needed and this uses
//less heap memory (or no heap memory) so this is safer if
//used in a low memory situation
void f() { throw string("foo"); }

//Valid, but avoid manual memory management if there's no reason to use it
void g() { throw new string("foo"); }

//Best.  Just a pointer to a string literal, so no allocation is needed,
//saving on cleanup, and removing a chance for an allocation to fail.
void h() { throw "foo"; }

int main() {
  try { f(); } catch (string s) { cout << s << endl; }
  try { g(); } catch (string* s) { cout << *s << endl; delete s; }
  try { h(); } catch (const char* s) { cout << s << endl; }
  return 0;
}

Bạn nên thích h thành f thành g. Lưu ý rằng trong tùy chọn ít thích hợp nhất, bạn cần giải phóng bộ nhớ một cách rõ ràng.


1
Nhưng không phải việc ném một const charcon trỏ đến một biến cục bộ là một lỗi? Vâng, tất nhiên tôi biết rằng trình biên dịch sẽ đặt c-string trong phần chưa được sửa đổi sẽ không thay đổi địa chỉ cho đến khi ứng dụng chạy. Nhưng nó là không xác định; hơn nữa, điều gì sẽ xảy ra nếu mã này nằm trong một thư viện đã biến mất ngay sau khi gặp lỗi? Btw, tôi cũng đã làm nhiều điều tồi tệ như vậy trong dự án của mình, tôi chỉ là một sinh viên. Nhưng tôi nên nghĩ về nó ...
Hi-Angel

1
@ Hi-Angel Không có biến cục bộ; những gì được ném ra đó là một chuỗi ký tự, có cách xử lý cụ thể và được xác định rõ bởi Tiêu chuẩn về thời gian tồn tại và mối quan tâm của bạn là tranh luận. Xem ví dụ: stackoverflow.com/a/32872550/2757035 Nếu có vấn đề ở đây, về cơ bản thì không có việc ném thông báo nào có thể hoạt động được (ít nhất là không cần phải nhào lộn / mạo hiểm quá mức), vì vậy tất nhiên là không, và nó ổn .
underscore_d

8

Nó hoạt động, nhưng tôi sẽ không làm điều đó nếu tôi là bạn. Có vẻ như bạn sẽ không xóa dữ liệu heap đó khi hoàn tất, điều đó có nghĩa là bạn đã tạo ra một rò rỉ bộ nhớ. Trình biên dịch C ++ quan tâm đến việc đảm bảo rằng dữ liệu ngoại lệ được giữ nguyên ngay cả khi ngăn xếp được bật lên, vì vậy đừng cảm thấy rằng bạn cần sử dụng heap.

Ngẫu nhiên, ném std::stringkhông phải là cách tốt nhất để bắt đầu. Bạn sẽ linh hoạt hơn rất nhiều nếu sử dụng một đối tượng bao bọc đơn giản. Nó có thể chỉ gói gọn stringtrong hiện tại, nhưng có thể trong tương lai bạn sẽ muốn bao gồm thông tin khác, như một số dữ liệu gây ra ngoại lệ hoặc có thể là số dòng (rất phổ biến, điều đó). Bạn không muốn thay đổi tất cả các xử lý ngoại lệ của mình ở mọi vị trí trong cơ sở mã của bạn, vì vậy hãy đi theo con đường cao ngay bây giờ và đừng ném các đối tượng thô.


8

Ngoài việc có thể ném thứ gì đó bắt nguồn từ std :: exception, bạn nên ném các tệp tạm thời ẩn danh và nắm bắt bằng cách tham khảo:

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw std::string("it's the end of the world!");
  }
}

void Foo:Caller(){
  try{
    this->Bar();// should throw
  }catch(std::string& caught){ // not quite sure the syntax is ok here...
    std::cout<<"Got "<<caught<<std::endl;
  }
}
  • Bạn nên ném thời gian tạm thời ẩn danh để trình biên dịch xử lý thời gian tồn tại của đối tượng của bất kỳ thứ gì bạn đang ném - nếu bạn ném thứ gì đó mới ra khỏi đống, người khác cần giải phóng thứ đó.
  • Bạn nên bắt các tham chiếu để ngăn đối tượng bị cắt

.

Xem "C ++ hiệu quả - ấn bản thứ 3" của Meyer để biết chi tiết hoặc truy cập https://www.securecoding.cert.org/.../ERR02-A.+Throw+anonymous+temporaries+and+catch+by+reference


5

Cách đơn giản nhất để ném Exception trong C ++:

#include <iostream>
using namespace std;
void purturb(){
    throw "Cannot purturb at this time.";
}
int main() {
    try{
        purturb();
    }
    catch(const char* msg){
        cout << "We caught a message: " << msg << endl;
    }
    cout << "done";
    return 0;
}

Bản in này:

We caught a message: Cannot purturb at this time.
done

Nếu bạn bắt được ngoại lệ được ném ra, thì ngoại lệ sẽ được chứa và chương trình sẽ tiếp tục. Nếu bạn không bắt được ngoại lệ, thì chương trình tồn tại và in:

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.


3
Đây có vẻ là một ý tưởng tồi - catch (std::exception&)bạn sẽ không nắm bắt được nó.
Timmmm

1

Mặc dù câu hỏi này khá cũ và đã được trả lời, tôi chỉ muốn thêm một lưu ý về cách xử lý ngoại lệ thích hợp trong C ++ 11 :

Sử dụng std::nested_exceptionstd::throw_with_nested

Theo ý kiến ​​của tôi, việc sử dụng chúng dẫn đến thiết kế ngoại lệ rõ ràng hơn và không cần thiết phải tạo hệ thống phân cấp lớp ngoại lệ.

Lưu ý rằng điều này cho phép bạn truy xuất dấu vết về các ngoại lệ bên trong mã của bạn mà không cần trình gỡ lỗi hoặc ghi nhật ký rườm rà. Nó được mô tả trên StackOverflow tại đâyđây , cách viết một trình xử lý ngoại lệ thích hợp sẽ tạo lại các ngoại lệ lồng nhau.

Vì bạn có thể làm điều này với bất kỳ lớp ngoại lệ dẫn xuất nào, bạn có thể thêm rất nhiều thông tin vào một backtrace như vậy! Bạn cũng có thể xem qua MWE của tôi trên GitHub , nơi mà backtrace sẽ trông giống như sau:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
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.