2010-12-29 18 views
7

Đôi khi, tôi nhận thấy một số mẫu mã hóa mà tôi đã có trong nhiều năm và nó làm tôi lo lắng. Tôi không có một vấn đề cụ thể, nhưng tôi cũng không nhớ đủ về lý do tại sao tôi đã chấp nhận mẫu đó, và một số khía cạnh của nó dường như phù hợp với một số chống mẫu. Điều này gần đây đã xảy ra với tôi WRT cách một số mã của tôi sử dụng ngoại lệ. Điều đáng lo ngại liên quan đến các trường hợp tôi bắt ngoại lệ "bằng cách tham chiếu", xử lý nó theo cách tương tự như cách tôi xử lý một tham số cho một hàm. Một lý do để làm điều này là vì vậy tôi có thể có một hệ thống phân cấp thừa kế của các lớp ngoại lệ, và chỉ rõ kiểu bắt đầu chung hơn hoặc chính xác hơn tùy thuộc vào ứng dụng. Ví dụ, tôi có thể xác định ...Tôi có sử dụng mệnh đề bắt C++, gia đình của các lớp ngoại lệ và phá hủy lành mạnh không?

class widget_error {}; 
class widget_error_all_wibbly : public widget_error {}; 
class widget_error_all_wobbly : public widget_error {}; 

void wibbly_widget() 
{ 
    throw widget_error_all_wibbly(); 
} 

void wobbly_widget() 
{ 
    throw widget_error_all_wobbly(); 
} 

void call_unknown_widget (void (*p_widget)()) 
{ 
    try 
    { 
    p_widget(); 
    } 
    catch (const widget_error &p_exception) 
    { 
    // Catches either widget_error_all_wibbly or 
    // widget_error_all_wobbly, or a plain widget_error if that 
    // is ever thrown by anything. 
    } 
} 

này hiện đang lo lắng cho tôi vì tôi đã nhận thấy rằng một cá thể của lớp được xây dựng (như một phần của ném) trong vòng một chức năng, nhưng được tham chiếu (thông qua p_Exception catch-khoản "tham số") sau khi chức năng đó đã thoát. Điều này thường là một anti-pattern - một tham chiếu hoặc con trỏ tới một biến cục bộ hoặc tạm thời được tạo ra trong một hàm, nhưng bị loại bỏ khi hàm thoát, thường là một tham chiếu/con trỏ lơ lửng vì biến cục bộ/tạm thời bị hủy và bộ nhớ được giải phóng khi hàm thoát. Một số bài kiểm tra nhanh cho thấy rằng ném ở trên có lẽ là OK - trường hợp được xây dựng trong mệnh đề throw không bị hủy khi hàm thoát, nhưng bị hủy khi mệnh đề catch xử lý nó hoàn tất - trừ khi khối catch catch rethrows ngoại lệ, trong trường hợp đó khối catch tiếp theo thực hiện công việc này.

Sự lo lắng còn lại của tôi là do thử nghiệm trong một hoặc hai trình biên dịch không có bằng chứng về tiêu chuẩn nói, và vì kinh nghiệm của tôi nói rằng điều tôi nghĩ là thông thường khác với ngôn ngữ đảm bảo.

Vậy - mẫu này có xử lý ngoại lệ (bắt chúng bằng cách sử dụng loại tham chiếu) an toàn không? Hoặc tôi có nên làm điều gì đó khác, chẳng hạn như ...

  • Đánh dấu (và xóa một cách rõ ràng) con trỏ tới các phiên bản được cấp phát thay vì tham chiếu đến nội dung trông giống như khi tạm thời?
  • Sử dụng lớp con trỏ thông minh?
  • Sử dụng mệnh đề bắt "pass-by-value" và chấp nhận rằng tôi không thể bắt bất kỳ lớp ngoại lệ nào từ một hệ thống phân cấp với một mệnh đề catch?
  • Điều tôi chưa từng nghĩ đến?
+2

Ngoài ra [sử dụng thừa kế ảo] (http://www.boost.org/community/error_handling.html). – ybungalobill

+1

@ybungalobill - Tôi nghĩ đó là một trò đùa, sau đó tôi đọc liên kết. Điểm thú vị. – Steve314

Trả lời

9

Điều này là ổn. Nó thực sự tốt để bắt ngoại lệ bởi tham chiếu liên tục (và xấu để bắt con trỏ). Bắt theo giá trị tạo ra một bản sao không cần thiết. Trình biên dịch đủ thông minh để xử lý ngoại lệ (và sự hủy diệt của nó) đúng cách - chỉ cần không cố gắng sử dụng tham chiếu ngoại lệ bên ngoài khối catch của bạn ;-)

Thực tế, điều tôi thường làm là kế thừa hệ thống phân cấp từ std :: runtime_error (được kế thừa từ std :: exception). Sau đó, tôi có thể sử dụng .what() và sử dụng ít khối catch hơn trong khi xử lý nhiều ngoại lệ hơn.

+0

+1 tất cả các vòng cho câu trả lời hữu ích. Tôi bị rách trên đó là hữu ích nhất, nhưng cuối cùng đã quyết định chấp nhận điều này. – Steve314

6

Mẫu này chắc chắn là an toàn.

Có các quy tắc đặc biệt kéo dài tuổi thọ của đối tượng được ném. Có hiệu quả, nó tồn tại miễn là nó đang được xử lý và nó được đảm bảo tồn tại cho đến cuối khối catch cuối cùng xử lý nó.

Một thành ngữ rất phổ biến, ví dụ, để lấy các ngoại lệ tùy chỉnh từ std::exception, ghi đè hàm what() thành viên của nó và bắt nó theo tham chiếu để bạn có thể in các thông báo lỗi từ nhiều trường hợp ngoại lệ khác nhau.

4

Có. Càng xa càng tốt.

Cá nhân tôi sử dụng std :: runtime_error làm cơ sở của tất cả các ngoại lệ calsses. Nó xử lý các thông báo lỗi, v.v.

Cũng không khai báo nhiều ngoại lệ mà bạn cần. Xác định ngoại lệ chỉ cho những thứ thực sự có thể bị bắt và sửa. Sử dụng một ngoại lệ chung chung hơn cho những thứ không thể bị bắt hoặc cố định.

Ví dụ: Nếu tôi phát triển một thư viện A. Sau đó, tôi sẽ có một AException bắt nguồn từ std :: runtime_error. Ngoại lệ này sẽ được sử dụng cho tất cả các trường hợp ngoại lệ chung từ thư viện. Đối với bất kỳ trường hợp ngoại lệ cụ thể nào mà người dùng của thư viện thực sự có thể bắt và làm điều gì đó (sửa hoặc giảm thiểu) với ngoại lệ thì tôi sẽ tạo một ngoại lệ cụ thể bắt nguồn từ AException (nhưng chỉ khi có điều gì đó có thể được thực hiện với ngoại lệ).

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