Исключительная безопасность с конструктором shared_ptr

В Effective C++ 3/E я прочитал это:

Это исключительный небезопасный код:

class Test { };
void foo(const std::shared_ptr<Test> &ptr, int i);
int bar();
...

foo(std::shared_ptr<Test>(new Test), bar());

Потому что компилятор может реализовать так:

  1. бежать new Test
  2. вызов bar() <- если bar() выдает исключение, объект Test выделено new Test не может быть удалено
  3. вызов конструктора std::shared_ptr<Test>
  4. вызов foo()

Но в этом случае компилятор может знать, что есть утечка памяти. Компилятор не может сделать delete автоматически, если выдается исключение?

Кроме того, компилятор делает автоматически delete в таком случае:

Test *p = new Test;

это реализовано так:

  1. вызов operator new выделить память
  2. вызов конструктора Test, Если конструктор выдает исключение, память автоматически удаляется.

Почему компилятор не работает в первом случае, в отличие от второго?

1 ответ

Решение

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

Стандарт мог бы указать, что исключение вызоветdelete быть вызванным на все объекты, обновленные в законченном выражении, через которое оно проходит. Есть ряд исторических причин, почему это даже не рассматривалось. И сегодня это, вероятно, сломало бы слишком много существующего кода.

Стандарт также мог бы наложить строгий порядок на оценку выражения. Это устранит множество проблем с неопределенным поведением и сделает код более детерминированным (поэтому тесты будут более надежными). Исторически C не использовал этот маршрут из-за его влияния на оптимизацию; С ++ сохранил правило.

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