RAII обычно лучше, но вы можете легко иметь семантику finally в C ++. Используя небольшое количество кода.
Кроме того, основные рекомендации C ++ дают окончательно.
Вот ссылка на реализацию GSL Microsoft и ссылка на реализацию Martin Moene
Бьярн Страуструп несколько раз говорил, что все, что есть в GSL, в конечном итоге должно войти в стандарт. Так что это должен быть ориентированный на будущее способ использования finally .
Вы можете легко реализовать себя, если хотите, продолжайте читать.
В C ++ 11 RAII и лямбда-выражения позволяют окончательно сделать генерал:
namespace detail { //adapt to your "private" namespace
template <typename F>
struct FinalAction {
FinalAction(F f) : clean_{f} {}
~FinalAction() { if(enabled_) clean_(); }
void disable() { enabled_ = false; };
private:
F clean_;
bool enabled_{true}; }; }
template <typename F>
detail::FinalAction<F> finally(F f) {
return detail::FinalAction<F>(f); }
пример использования:
#include <iostream>
int main() {
int* a = new int;
auto delete_a = finally([a] { delete a; std::cout << "leaving the block, deleting a!\n"; });
std::cout << "doing something ...\n"; }
вывод будет:
doing something...
leaving the block, deleting a!
Лично я использовал это несколько раз, чтобы обеспечить закрытие дескриптора файла POSIX в программе на C ++.
Обычно лучше иметь реальный класс, который управляет ресурсами и, таким образом, избегает любых утечек, но этот , наконец, полезен в тех случаях, когда создание класса звучит как перебор.
Кроме того, мне нравится это лучше, чем другие языки наконец , потому что при естественном использовании вы пишете закрывающий код рядом с кодом открытия (в моем примере new и delete ), а разрушение следует за конструкцией в порядке LIFO, как обычно в C ++. Единственным недостатком является то, что вы получаете автоматическую переменную, которую вы на самом деле не используете, а лямбда-синтаксис делает ее немного шумной (в моем примере в четвертой строке только слово finally и блок {} на права имеют смысл, остальное по сути шум).
Другой пример:
[...]
auto precision = std::cout.precision();
auto set_precision_back = finally( [precision, &std::cout]() { std::cout << std::setprecision(precision); } );
std::cout << std::setprecision(3);
Элемент disable полезен, если finally необходимо вызывать только в случае сбоя. Например, вам нужно скопировать объект в три разных контейнера, вы можете настроить finally , чтобы отменить каждую копию и отключить ее после того, как все копии будут успешными. Поступая так, если уничтожение не может бросить, вы гарантируете сильную гарантию.
отключить пример:
//strong guarantee
void copy_to_all(BIGobj const& a) {
first_.push_back(a);
auto undo_first_push = finally([first_&] { first_.pop_back(); });
second_.push_back(a);
auto undo_second_push = finally([second_&] { second_.pop_back(); });
third_.push_back(a);
//no necessary, put just to make easier to add containers in the future
auto undo_third_push = finally([third_&] { third_.pop_back(); });
undo_first_push.disable();
undo_second_push.disable();
undo_third_push.disable(); }
Если вы не можете использовать C ++ 11, у вас все равно может быть наконец , но код становится немного длиннее. Просто определите структуру только с помощью конструктора и деструктора, конструктор берет ссылки на все, что нужно, а деструктор выполняет необходимые вам действия. Это в основном то, что делает лямбда, сделанная вручную.
#include <iostream>
int main() {
int* a = new int;
struct Delete_a_t {
Delete_a_t(int* p) : p_(p) {}
~Delete_a_t() { delete p_; std::cout << "leaving the block, deleting a!\n"; }
int* p_;
} delete_a(a);
std::cout << "doing something ...\n"; }