Есть ли предпочтительная идиома для имитации попытки / окончательного использования Java в C ++? - PullRequest
13 голосов
/ 01 февраля 2009

Я занимался Java уже много лет, поэтому не отслеживал C ++. Было ли добавлено предложение finally к обработке исключений в C ++ в определении языка?

Есть ли любимая идиома, которая имитирует попытку / окончание Java?

Я также обеспокоен тем, что в C ++ нет окончательного супертипа для всех возможных исключений, которые могут быть выброшены - как класс Throwable в Java.

Я могу написать:

try {
  // do something
} catch(...) {
  // alas, can't examine the exception
  // can only do cleanup code and perhaps rethrow, ala:
  throw;
}

ДОПОЛНИТЕЛЬНОЕ РЕДАКТИРОВАНИЕ:

В итоге я принял ответ, который получил наибольшее количество голосов, т. е. использовать деструкторы, чтобы сделать уборку. Конечно, из моих собственных комментариев ясно, что я не совсем согласен с этим. Тем не менее, C ++ - это то, что есть, и так в приложение приложить у меня в ум, я собираюсь более или менее стремиться придерживаться общего сообщества практика. Я буду использовать шаблоны классов для обернуть ресурсы, которые еще не имеют деструктор класса (т.е. библиотека C ресурсы), таким образом, наделяя их семантика деструктора.

ИЗМЕНЕНИЕ НОВОГО ДОПОЛНЕНИЯ:

Хм, вместо наконец затем закрытие особенность возможно? Закрытие в сочетании с Подход ScopeGuard (см. Один из ответы ниже) будет способ выполнить очистку с произвольным действия и доступ к очистке контекст внешней области кода. Очистка может быть выполнена идиомным способом, который наблюдается в программировании на Ruby, где они предоставляют блоки очистки при открытии ресурса. Не является особенность закрытия рассматривается для C ++?

Ответы [ 15 ]

1 голос
/ 01 февраля 2009

Я думаю, что вам не хватает того, что может catch (...) сделать.

Вы говорите в своем примере "увы, не можете изучить исключение". Ну, у вас нет информации о типе исключения. Вы даже не знаете, является ли это полиморфным типом, поэтому даже если бы у вас была какая-то нетипизированная ссылка на него, вы даже не могли бы безопасно попробовать dynamic_cast.

Если вы знаете об определенных исключениях или иерархиях исключений, с которыми можно что-то делать, то это место для блоков перехвата с расширенными именами типов.

catch (...) не часто используется в C ++. Он может использоваться в местах, которые должны гарантировать, что они не бросают или только выбрасывают определенные контрактные исключения. Если вы используете catch (...) для очистки, то очень велика вероятность того, что ваш код не является надежно исключительным в любом случае.

Как уже упоминалось в других ответах, если вы используете локальные объекты для управления ресурсами (RAII), то это может удивить и пролить свет на то, как мало блоков catch вам нужно, часто - если вам не нужно ничего делать локально, за исключением - даже блок try может быть избыточным, поскольку вы позволяете исключениям перетекать в клиентский код, который может реагировать на них, при этом гарантируя отсутствие проблем с ресурсами.

Чтобы ответить на ваш исходный вопрос, если вам нужен какой-то фрагмент кода для выполнения в конце блока, исключение или отсутствие исключения, тогда рецепт будет.

class LocalFinallyReplacement {
    ~LocalFinallyReplacement() { /* Finally code goes here */ }
};
// ...
{ // some function...
    LocalFinallyReplacement lfr; // must be a named object

    // do something
}

Обратите внимание, как мы можем полностью покончить с try, catch и throw.

Если у вас есть данные в функции, которые были первоначально объявлены вне блока try, к которым вам нужен доступ в блоке "finally", то вам может потребоваться добавить их в конструктор вспомогательного класса и сохранять их до деструктора. , Однако на этом этапе я бы серьезно пересмотрел вопрос о том, можно ли решить проблему, изменив дизайн локальных объектов обработки ресурсов, поскольку это может привести к ошибкам в дизайне.

1 голос
/ 01 февраля 2009

C ++ деструкторы делают finally избыточным. Вы можете получить тот же эффект, переместив код очистки, наконец, в соответствующие деструкторы.

0 голосов
/ 13 марта 2009

Думаю, я бы добавил к этому свое собственное решение - своего рода интеллектуальную оболочку для указателя, когда вам приходится иметь дело с не-RAII типами.

Используется так:

Finaliser< IMAPITable, Releaser > contentsTable;
// now contentsTable can be used as if it were of type IMAPITable*,
// but will be automatically released when it goes out of scope.

Итак, вот реализация Финализатора:

/*  Finaliser
    Wrap an object and run some action on it when it runs out of scope.
    (A kind of 'finally.')
    * T: type of wrapped object.
    * R: type of a 'releaser' (class providing static void release( T* object )). */
template< class T, class R >
class Finaliser
{
private:
    T* object_;

public:
    explicit Finaliser( T* object = NULL )
    {
        object_ = object;
    }

    ~Finaliser() throw()
    {
        release();
    }

    Finaliser< T, R >& operator=( T* object )
    {
        if (object_ != object && object_ != NULL)
        {
            release();
        }
        object_ = object;

        return *this;
    }

    T* operator->() const
    {
        return object_;
    }

    T** operator&()
    {
        return &object_;
    }

    operator T*()
    {
        return object_;
    }

private:
    void release() throw()
    {
        R::release< T >( object_ );
    }
};

... и вот Релизер:

/*  Releaser
    Calls Release() on the object (for use with Finaliser). */
class Releaser
{
public:
    template< class T > static void release( T* object )
    {
        if (object != NULL)
        {
            object->Release();
        }
    }
};

У меня есть несколько таких релизов, включая один для free () и один для CloseHandle ().

0 голосов
/ 02 февраля 2009

Относительно вашего дополнения-редактирования да закрытие рассматривается для C ++ 0x. Их можно использовать с защитными ограждениями RAII, чтобы обеспечить простое в использовании решение. Проверьте блог Pizer Их также можно использовать для имитации try-finally, см. этот ответ ; но это действительно хорошая идея? .

0 голосов
/ 01 февраля 2009

За эти 15 лет я много занимался дизайном классов и оболочек шаблонов на C ++ и делал все это на C ++ с точки зрения очистки деструкторов. Каждый проект, тем не менее, также неизменно включал использование библиотек C, предоставляющих ресурсы с открытым, используйте его, закрывайте модель использования. Попытка / наконец будет означать, что такой ресурс можно просто использовать там, где он должен быть - абсолютно надежным способом - и покончить с ним. Наименьший утомительный подход к программированию такой ситуации. Может иметь дело со всеми другими состояниями, происходящими во время логики этой очистки, без необходимости находить область действия в каком-то деструкторе-обертке.

Я выполнил большую часть своего C ++ кодирования в Windows, поэтому всегда мог прибегнуть к использованию __try / __ от Microsoft для таких ситуаций. (Их структурированная обработка исключений имеет некоторые мощные возможности для взаимодействия с исключениями.) Увы, похоже, что язык Си никогда не ратифицировал какие-либо переносимые конструкции обработки исключений.

Однако это не было идеальным решением, потому что было непросто смешать код C и C ++ в блоке try, в котором может быть выброшен любой стиль исключения. Блок finally, добавленный в C ++, помог бы в таких ситуациях и обеспечил бы переносимость.

...