Когда я что-то «выбрасываю», где это хранится в памяти? - PullRequest
82 голосов
/ 07 июля 2011

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

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

Ответы [ 5 ]

51 голосов
/ 07 июля 2011

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

Быстрый эксперимент с моим компилятором (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));
}

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

20 голосов
/ 07 июля 2011

С этой страницы :

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

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

4 голосов
/ 07 июля 2011

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

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

0 голосов
/ 07 июля 2011

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

0 голосов
/ 07 июля 2011

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

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