Отлов по ссылке, когда переменная исключения не определена - PullRequest
2 голосов
/ 13 сентября 2011

При отлове исключения стандартное указание - бросок по значению, улов по ссылке. Насколько я понимаю, это по двум причинам:

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

Если у нас есть сценарий, в котором мы не определяем имя исключения в блоке catch, эти проблемы (на самом деле 1., поскольку нарезка не будет проблемой, если у нас нет имени для переменной), все равно действительный

Например:

catch(my_exception)
{ ... }

или

catch(my_exception &)
{ ... }

Есть ли еще возможность завершения программы, если в этом случае исключение перехватывается значением? Я чувствую, что это технически все еще возможно.

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

Ответы [ 3 ]

5 голосов
/ 13 сентября 2011

Стандарт не требует специальной оптимизации в случае безымянного объекта исключения. Наоборот, тогда требуется эффект, как если бы временный объект был инициализирован копией. Это копирование может привести к динамическому выделению памяти.

N3290 §15.3 / 16 :
Если объявление-исключение указывает имя, оно объявляет переменную, которая инициализируется копией (8.5) из объект исключения. Если объявление-исключение обозначает тип объекта, но не указывает имя, временный (12.2) инициализируется копированием (8.5) из объекта исключения. Время жизни переменной или временное заканчивается при выходе из обработчика, после уничтожения любых автоматических объектов, инициализированных в обработчике.

В приведенном выше параграфе не упоминается отлов по ссылке, и поэтому можно разумно сделать вывод, что он применяется независимо от того, перехвачен ли объект исключения по ссылке; что копия все равно создается.

Однако это противоречит следующему абзацу:

N3290 §15,3 / 17 :
Когда обработчик объявляет непостоянный объект, любые изменения этого объекта не влияют на временный объект, который был инициализирован выполнением throw-выражения . Когда обработчик объявляет ссылку на непостоянный объект, любые изменения в ссылочном объекте являются изменениями во временном объекте, инициализированном когда throw-выражение было выполнено и будет иметь эффект, если этот объект будет переброшен.

Итак, объявленный тип T&T non- const) является единственным случаем, когда C ++ 11 требует ссылки непосредственно на брошенный объект вместо копирования. И это так же в C ++ 03, за исключением того, что в C ++ 03 есть некоторые дополнительные формулировки об оптимизации «как будто». Таким образом, для формального предпочтение должно быть для

    catch( T& name )

и

    catch( T& )

Однако я всегда ловил исключения вроде catch( T const& ). С практической точки зрения можно предположить, что компилятор оптимизирует это для прямой ссылки на брошенный объект, даже если можно придумать случаи, когда наблюдаемый программный эффект был бы тогда нестандартным. Например <evil grin>

#include <stdio.h>
#include <stdexcept>

struct Error
    : std::runtime_error
{
public:
    static Error* pThrown;

    char const* pMessage_;
    Error()
        : std::runtime_error( "Base class message" )
        , pMessage_( "Original message." )
    {
        printf( "Default-construction of Error object.\n" );
        pThrown = this;
    }

    Error( Error const& other )
        : std::runtime_error( other )
        , pMessage_( other.pMessage_ )
    {
        printf( "Copy-construction of Error obejct.\n" );
    }

    char const* what() const throw() { return pMessage_; }
};

Error*  Error::pThrown  = 0;

int main()
{
    printf( "Testing non-const ref:\n" );
    try
    {
        throw Error();
    }
    catch( Error& x )
    {
        Error::pThrown->pMessage_ = "Modified message.";
        printf( "%s\n", x.what() );
    }

    printf( "\n" );
    printf( "Testing const ref:\n" );
    try
    {
        throw Error();
    }
    catch( Error const& x )
    {
        Error::pThrown->pMessage_ = "Modified message";
        printf( "%s\n", x.what() );
    }
}

Как для MinGW g ++ 4.4.1, так и для Visual C ++ 10.0 выводом является & hellip;

Testing non-const ref:
Default-construction of Error object.
Modified message.

Testing const ref:
Default-construction of Error object.
Modified message

Педантичный формалист мог бы сказать, что оба компилятора не соответствуют друг другу, и ему не удалось создать копию для случая Error const&. Чисто практичный практикующий может сказать, что, эй, а что еще вы ожидали 1048 *? И мне, я говорю, что формулировка в стандарте здесь очень далека от совершенства, и что, если что-то и следует ожидать, следует внести ясность, чтобы явно разрешить вывод, приведенный выше, так что отлов по ссылке const является одновременно безопасным и максимально эффективный.

Подводя итог по отношению к вопрос ОП:

  • Перехват по ссылке не вызовет конструктор копирования, который потенциально может завершить программу.

  • Стандарт гарантирует это только для ссылок, отличных от const.

  • На практике, как показано, это также гарантируется для ссылки на const, даже когда это влияет на результаты программы.

Приветствия и hth.,

1 голос
/ 13 сентября 2011

С вашим исключением могут быть невидимые данные, например, vtable .

vtable - это невидимая структура данных, которая содержит информацию о том, где можно найти определенные полиморфные (т.е. virtual) функции-члены. Эта таблица в общем случае стоит немного памяти, которая содержится в самом объекте. Это может быть по размеру указателя на некоторую внешнюю таблицу или даже полную таблицу. Как всегда, это зависит.

1 голос
/ 13 сентября 2011

Я бы предпочел ловить по ссылке.Компилятор может отбросить исключение как оптимизацию и не копировать, но это только возможность .Ловля по ссылке дает вам уверенность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...