Когда я "бросаю" что-то, где он хранится в памяти?

Я понимаю, что когда что-то есть throw n, стек "разматывается" до точки, где он пойман, и запускаются деструкторы экземпляров класса в стеке в каждом функциональном контексте (вот почему вы не должны вытащите исключение из деструктора - вы можете в конечном итоге выбрасывать второй)... но мне интересно, где в памяти объект, который я выбрал, сохраняется, когда это происходит?

Является ли она зависимой от реализации? Если да, существует ли какой-то конкретный метод, используемый большинством популярных компиляторов?

Ответ 1

Да, ответ зависит от компилятора.

Быстрый эксперимент с моим компилятором (g++ 4.4.3) показывает, что его библиотека времени выполнения сначала пытается malloc сохранить память для исключения и, в противном случае, пытается выделить пространство в рамках "аварийного буфера" всей системы, сегмент данных. Если это не сработает, он вызывает std::terminate().

Похоже, что основной задачей аварийного буфера является возможность выброса std::bad_alloc после того, как процесс завершился из кучного пространства (в этом случае вызов malloc завершился с ошибкой).

Соответствующая функция __cxa_allocate_exception:

extern "C" void *
__cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) throw()
{
  void *ret;

  thrown_size += sizeof (__cxa_refcounted_exception);
  ret = malloc (thrown_size);

  if (! ret)
    {
      __gnu_cxx::__scoped_lock sentry(emergency_mutex);

      bitmask_type used = emergency_used;
      unsigned int which = 0;

      if (thrown_size > EMERGENCY_OBJ_SIZE)
        goto failed;
      while (used & 1)
        {
          used >>= 1;
          if (++which >= EMERGENCY_OBJ_COUNT)
            goto failed;
        }

      emergency_used |= (bitmask_type)1 << which;
      ret = &emergency_buffer[which][0];

    failed:;

      if (!ret)
        std::terminate ();
    }

  // We have an uncaught exception as soon as we allocate memory.  This
  // yields uncaught_exception() true during the copy-constructor that
  // initializes the exception object.  See Issue 475.
  __cxa_eh_globals *globals = __cxa_get_globals ();
  globals->uncaughtExceptions += 1;

  memset (ret, 0, sizeof (__cxa_refcounted_exception));

  return (void *)((char *)ret + sizeof (__cxa_refcounted_exception));
}

Я не знаю, насколько типична эта схема.

Ответ 2

От эта страница:

Хранение необходимо для исключений выброшены. Это хранилище должно сохраняться в то время как стек разматывается, так как он будет использоваться обработчиком и быть потокобезопасным. Объект исключения поэтому хранение обычно будет выделено в куче, хотя реализации могут обеспечить аварийный буфер для поддержки метания Исключения bad_alloc при низкой памяти условия.

Теперь это только Itanium ABI, и я ищу подробности, относящиеся к GCC, Clang и MSVC. Тем не менее, стандарт ничего не указывает, и это, по-видимому, является очевидным способом реализации хранилища исключений, поэтому...

Ответ 3

Я не знаю, ответит ли это на ваш вопрос, но this (как компилятор С++ реализует обработку исключений) отличная статья об обработке исключений:. Я очень рекомендую его (:

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

Ответ 4

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

Ответ 5

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