Tôi quen thuộc với những ưu điểm của RAII, nhưng gần đây tôi vấp một vấn đề trong mã như thế này:Làm thế nào để xử lý thất bại constructor cho RAII
class Foo
{
public:
Foo()
{
DoSomething();
...
}
~Foo()
{
UndoSomething();
}
}
Tất cả tốt, ngoại trừ mã trong phần constructor ...
đã ném một ngoại lệ, với kết quả là UndoSomething()
không bao giờ được gọi.
Có những cách rõ ràng để khắc phục sự cố cụ thể đó, như quấn ...
trong khối try/catch rồi gọi UndoSomething()
, nhưng: đó là mã trùng lặp và b: khối try/catch là một mã mà tôi thử tránh bằng cách sử dụng kỹ thuật RAII. Và, mã có khả năng trở nên tồi tệ hơn và dễ bị lỗi hơn nếu có nhiều cặp Do/Undo liên quan, và chúng ta phải dọn dẹp nửa chừng.
Tôi tự hỏi có cách tiếp cận tốt hơn để thực hiện điều này - có thể một đối tượng riêng biệt sẽ lấy một con trỏ hàm và gọi hàm khi nó bị hủy?
class Bar
{
FuncPtr f;
Bar() : f(NULL)
{
}
~Bar()
{
if (f != NULL)
f();
}
}
Tôi biết rằng sẽ không biên dịch nhưng nó sẽ hiển thị nguyên tắc. Foo sau đó trở thành ...
class Foo
{
Bar b;
Foo()
{
DoSomething();
b.f = UndoSomething;
...
}
}
Lưu ý rằng foo bây giờ không yêu cầu hủy. Điều đó nghe có vẻ rắc rối hơn nó đáng giá, hay đây là một mô hình chung với thứ gì đó hữu ích trong việc tăng cường xử lý việc nâng hạng nặng cho tôi?
try/catch là _not_ code smell và thường không được sử dụng IMO. –
xem tại đây: http://www.parashift.com/c++-faq-lite/selfcleaning-members.html – MadScientist
@MooingDuck: Thật vậy, bản thân chúng không có mùi. Nhưng 'try {} catch (...) {throw;} 'có mùi khá mạnh. –