Как работает эта реализация на цепочке исключения?

StackOverflow https://stackoverflow.com/questions/3566774

Вопрос

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

Код включен ниже, и я четко пометил каждый раздел, который я не получаю. Описание того, что я не понимаю, включен ниже кода. Код был написан Потаtoswatter..


Код


struct exception_data { // abstract base class; may contain anything
    virtual ~exception_data() {}
};

struct chained_exception : std::exception {
    chained_exception( std::string const &s, exception_data *d = NULL )
        : data(d), descr(s) {
        try {
            link = new chained_exception;

            // ----------------------------------------------------------------
            //   How does this work (section 1)?
            throw;
            // ----------------------------------------------------------------

        } catch ( chained_exception &prev ) {
            // ----------------------------------------------------------------
            //   How does this work (section 2)?
            swap( *link, prev );
            // ----------------------------------------------------------------
        } // catch std::bad_alloc somehow...
    }

    friend void swap( chained_exception &lhs, chained_exception &rhs ) {
        std::swap( lhs.link, rhs.link );
        std::swap( lhs.data, rhs.data );
        swap( lhs.descr, rhs.descr );
    }

    virtual char const *what() const throw() { return descr.c_str(); }

    virtual ~chained_exception() throw() {
        // --------------------------------------------------------------------
        //   How does this work (section 3)?
        if ( link && link->link ) delete link; // do not delete terminator
        // --------------------------------------------------------------------
        delete data;
    }

    chained_exception *link; // always on heap
    exception_data *data; // always on heap
    std::string descr; // keeps data on heap

private:
    chained_exception() : link(), data() {}
    friend int main();
};

void f() {
    try {
        throw chained_exception( "humbug!" );
    } catch ( std::exception & ) {
        try {
            throw chained_exception( "bah" );
        } catch ( chained_exception &e ) {
            chained_exception *ep = &e;
            for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
                // Print ep->what() to std::cerr
            }
        }
    }

    try {
        throw chained_exception( "meh!" );
    } catch ( chained_exception &e ) {
        for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
            // Print ep->what() to std::cerr
        }
    }
}

int main() try {
    // ------------------------------------------------------------------------
    //   How does this work (section 4)?
    throw chained_exception(); // create dummy end-of-chain
    // ------------------------------------------------------------------------
} catch( chained_exception & ) {
    // body of main goes here
    f();
}

Запуск кода дает следующий выход:

bah
humbug!
meh!

Что я не понимаю

  1. throw; внутри try-блокировать: Я никогда не видел этого раньше. Единственное место, где я подумал throw; быть действительным, был внутри catch-Бук к ритроу, что было поймано. Так что это делает? Некоторые отладки, видимо, показывают, что брошенное исключение - это то, что было брошено ранее, но это было внутри совершенно другой try-блокировать. На самом деле, это было даже за пределами struct Декларация!

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

  3. Проверить link а также linkСсылка: Я могу понять, проверяя, что link не является NULL (Хотя даже удаление NULL Указатель не имеет эффекта), но почему нужно проверить linkссылка?

  4. Бросить деминируемое исключение: Почему этот манекен нужен? Это брошено, но затем бросилось. Почему нам нужно это как конец цепочке?

Это было полезно?

Решение

Умный код - kudos to ptatoswatter на этом. Я думаю, что мне пришлось бы найти свой путь вокруг последнего предмета, хотя.

  1. throw; связаны активное исключение. Действительно только если catch блок на стеке. Я не могу вспомнить, где я наткнулся на этот тидбит, но это, вероятно, было на так в контексте какого-то другого вопроса. Голый бросок дает нам доступ к текущему исключему, поймаю его в chained_exception конструктор. Другими словами, prev В конструкторе является ссылкой на исключение, что мы в настоящее время обрабатываем.

  2. Вы правильно здесь. Это предотвращает двойное удаление.

  3. Sentinel исключение, один брошенный в main, никогда не следует удалить. Один идентифицирующий атрибут этого исключения в том, что это link член есть NULL.

  4. Это часть, которую я не люблю, но не могу придумать легко. Единственный видимый chained_exception Конструктор можно назвать только когда catch Блок активен. IIRC, голый бросок без активного catch Блок не-нет. Итак, обходной путь - бросить в main и поставить весь свой код в catch блокировать.

Теперь, если вы попробуете этот метод в мульти-резьбовом коде, убедитесь, что вы очень хорошо понимаете (4). Вам придется повторить это в вашей точке записи по ниве.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top