2015-10-10 15 views
9

Tôi đã đọc về tuyên bố của defer ngôn ngữ đi. Nó cho phép bạn chỉ định một hành động cần thực hiện khi một hàm kết thúc. Ví dụ, nếu bạn có một con trỏ tập tin hoặc tài nguyên, thay vì viết miễn phí/xóa với mọi đường dẫn trả về có thể, bạn chỉ cần xác định chức năng trì hoãn một lần.phong cách golang "trì hoãn" trong C++

Dường như một sự tương tự có thể đến với C++ sau cùng (What is standard defer/finalizer implementation in C++?, Will there be standardization of scope guard/scope exit idioms?) Cho đến lúc đó, có bất kỳ điều gì không lường trước được về việc thực hiện nó với một đối tượng có destructor gọi lại không? Nó trông giống như destructor order for local variables is sane và nó cũng xử lý ngoại lệ tốt, mặc dù có thể không xuất hiện trên các tín hiệu.

Dưới đây là triển khai mẫu ... có sự cố gì về nó không?

#include <iostream> 
#include <functional> 
using namespace std; 

class FrameExitTask { 
    std::function<void()> func_; 
public: 
    FrameExitTask(std::function<void()> func) : 
    func_(func) { 
    } 
    ~FrameExitTask() { 
     func_(); 
    } 
    FrameExitTask& operator=(const FrameExitTask&) = delete; 
    FrameExitTask(const FrameExitTask&) = delete; 
}; 

int main() { 
    FrameExitTask outer_task([](){cout << "world!";}); 
    FrameExitTask inner_task([](){cout << "Hello, ";}); 
    if (1+1 == 2) 
     return -1; 
    FrameExitTask skipped_task([](){cout << "Blam";}); 
} 

Output: Hello, world!

+2

Điều này có thể thích hợp hơn cho [CodeReview] (http://codereview.stackexchange.com). Lưu ý rằng đã có nhiều triển khai của lớp giống như ScopeGuard này, vậy tại sao lại phát minh ra bánh xe? Một số triển khai mà tôi biết có nhiều lợi ích hoặc công cụ chuyên biệt mà tôi muốn sử dụng cho phiên bản của bạn (ví dụ: không xóa được loại). – dyp

+2

Vấn đề với điều này là mỗi lớp nên đã làm những gì bạn đang làm trong destructor của họ để các trường hợp sử dụng trong C + + là cách hiếm hơn trong Go. Nếu bạn đang tìm kiếm hiện thực hiện kiểm tra boost.ScopeExit. Facebook của Folly có một cũng như tôi nghĩ. – inf

+1

Có lẽ bạn nên đánh dấu phần phá hủy 'noexcept'. Nếu hàm bạn sử dụng 'FrameExitTask' trả về bình thường, một ngoại lệ từ trình xử lý cuối cùng của bạn có thể sẽ hoạt động. Nếu hàm này thoát do một số ngoại lệ khác, thì ngoại lệ thứ hai từ trình xử lý của bạn sẽ gây ra sự cố. –

Trả lời

3

đã này tồn tại, và nó được gọi là phạm vi bảo vệ. Xem bài nói chuyện tuyệt vời này: https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C. Điều này cho phép bạn dễ dàng tạo ra một cuộc gọi tùy ý để được gọi khi thoát. Đây là phiên bản mới hơn; nó được phát triển rất lâu trước khi tồn tại.

Nó hoạt động hoàn hảo nói chung, nhưng tôi không chắc chắn những gì bạn có ý nghĩa bởi nó xử lý ngoại lệ. Ném ngoại lệ từ một chức năng mà phải được gọi là thoát khỏi phạm vi là một mớ hỗn độn. Lý do: khi một ngoại lệ được ném (và không bị bắt ngay lập tức), phạm vi thoát hiện tại. Tất cả các destructor đều chạy, và ngoại lệ sẽ tiếp tục lan truyền. Nếu một trong những kẻ hủy diệt ném, bạn sẽ làm gì? Bây giờ bạn có hai ngoại lệ trực tiếp.

Tôi cho rằng có những cách ngôn ngữ có thể cố gắng giải quyết vấn đề này, nhưng nó rất phức tạp. Trong C++, nó rất hiếm khi một destructor ném sẽ được coi là một ý tưởng tốt.

+0

Tôi có nghĩa là nó hoạt động chính xác khi một ngoại lệ được ném giữa chức năng, không phải trong gọi lại. Tìm hiểu về ScopeGuard là rất hữu ích. Cảm ơn! – daveagp

-1

Điều này đã tồn tại trong C++, và đó là một ý tưởng cực kỳ tồi tệ và ví dụ bạn đưa ra minh họa tại sao nó là một điều vô nghĩa và tôi hy vọng rằng Ủy ban không bao giờ giới thiệu nó.

Ví dụ, nếu bạn có một xử lý tệp, sau đó viết một lớp để làm điều đó cho bạn và sau đó bạn sẽ không phải viết một tuyên bố trì hoãn cho mỗi trường hợp sử dụng duy nhất mà bạn có thể dễ dàng quên. Hoặc chỉ đơn giản là làm cho nó sai. Bạn viết một destructor, một lần. Đó là nó. Sau đó, bạn được đảm bảo cho tất cả các ứng dụng của lớp đó là an toàn. Nó an toàn hơn nhiều và dễ dàng hơn nhiều.

7

Boost thảo luận điều này trong Smart Pointer Lập trình kỹ thuật:

Bạn có thể làm, ví dụ:

#include <memory> 
#include <iostream> 
#include <functional> 

using namespace std; 
using defer = shared_ptr<void>;  

int main() { 
    defer _(nullptr, bind([]{ cout << ", World!"; })); 
    cout << "Hello"; 
} 

Hoặc, mà không bind:

#include <memory> 
#include <iostream> 

using namespace std; 
using defer = shared_ptr<void>;  

int main() { 
    defer _(nullptr, [](...){ cout << ", World!"; }); 
    cout << "Hello"; 
} 

Bạn cũng có thể cũng sẽ giới thiệu một lớp học nhỏ của riêng bạn cho như vậy, hoặc tận dụng các thực hiện tham chiếu cho N3830/P0052:

The C++ Core Guidelines cũng have a guideline sử dụng chức năng gsl::finally, có chức năng thực hiện here.

Có nhiều codebases sử dụng các giải pháp tương tự cho việc này, do đó, có nhu cầu về công cụ này.

liên quan SO thảo luận: