Исключение безопасности нового оператора

Это реализация нового оператора в libstdc++:

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
  void *p;

  /* malloc (0) is unpredictable; avoid it.  */
  if (__builtin_expect (sz == 0, false))
    sz = 1;

  while ((p = malloc (sz)) == 0)
    {
      new_handler handler = std::get_new_handler ();
      if (! handler)
        _GLIBCXX_THROW_OR_ABORT(bad_alloc());
      handler ();
    }

  return p;
}

Кто гарантирует, что исключение в конструкторе освободит выделенную память?

Upd: спасибо комментаторам - на самом деле я имел в виду исключительную безопасность нового выражения.

Не могли бы вы привести пример? Откуда выбрасывается исключение — из new или из конструктора? Какая память - выделена под строящийся объект или какая-то память выделена внутри конструктора?

KamilCuk 10.12.2020 14:42

какой конструктор? новый оператор просто выделяет память, он не создает объекты.

bolov 10.12.2020 14:43

Какой конструктор?

Asteroids With Wings 10.12.2020 14:44

@bolov Оператор new наверняка вызывает конструктор (за исключением формы new T для тривиальных типов). Функция operator new не работает. Да, терминология C++ сбивает с толку.

Konrad Rudolph 10.12.2020 14:49

Таким образом, было бы лучше не использовать термин «новый оператор»; действительно, сам стандарт делает это лишь мимоходом, нечасто и таким образом, что лично я считаю недостатком. new — это ключевое слово, а выражение new T — это новое выражение.

Asteroids With Wings 10.12.2020 14:51

У нас есть «оператор новый» и «новое выражение». «Новые выражения» вызывают «оператор новый» (кроме нового размещения) для выделения памяти, а затем создают объект (объекты). Я имел в виду оператор new (который представлен в вопросе и не создает объекты). Действительно запутанно.

bolov 10.12.2020 14:55

Хорошо, возможно, я говорю о «новом выражении», а не о новом операторе. Как и где в коде реализована исключительная безопасность нового выражения?

Max Dmitrichenko 10.12.2020 15:27

@bolov «новое выражение» = «новый оператор». Я согласен с астероидами в том, что это сбивающий с толку термин, и его лучше избегать. Но он широко используется даже среди экспертов по C++, в том числе по стандарту C++.

Konrad Rudolph 10.12.2020 16:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
8
282
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы смешиваете в своем вопросе «новое выражение» и «новый оператор».

Новое выражение выглядит так: A* a = new A(); Язык C++ определяет, что это выражение оценивается примерно так (упрощенный псевдокод):

void* __a = operator new(sizeof(A));
try {
   A::A(this = static_cast<A*>(__a));
} catch (...) {  operator delete (__a); throw; }

Как видите, память освобождается, если происходит исключение.

Более подробное объяснение:

Если инициализация завершается выдачей исключения (например, из конструктор), если new-expression выделил какую-либо память, он вызывает соответствующая функция освобождения: оператор удаления для типа, отличного от массива, оператор delete[] для типа массива.


Реальный код, сгенерированный компилятором, смотрите здесь. Вы можете заметить вызов оператора delete, даже если исходный код его не содержит:

struct A { A(); };
A::A() { throw 13; }

int main()
{
    auto a = new A();
}

Ассемблер:

 call 401040 <operator new(unsigned long)@plt>
...
 call 401172 <A::A()>
...
 call 401050 <operator delete(void*, unsigned long)@plt>
...
 call 401080 <_Unwind_Resume@plt>

Но где код, реализующий такую ​​логику?

Max Dmitrichenko 10.12.2020 15:25

Генерируется компилятором

Chris Uzdavinis 10.12.2020 15:27

@ChrisUzdavinis, чтобы мы могли сказать, что любое новое выражение на самом деле заключено в блок try-catch? Или в этом случае компилятор делает это по-другому? Я понимаю, что это определяется реализацией, но давайте поговорим о самых популярных компиляторах, таких как gcc или clang?

Max Dmitrichenko 10.12.2020 15:39

@MaxDmitrichenko - я добавил ссылку на пример, сгенерированный из CompilerExplorer. Как именно он генерируется - зависит от поставщика компилятора. Не забывайте - этот код в первой части моего ответа является псевдокодом - не относитесь к нему слишком серьезно - это просто иллюстрация, а не реальный код.

PiotrNycz 10.12.2020 15:51

Другие вопросы по теме