В Эффективный 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());
Потому что компилятор может реализовать так:
- бежать
new Test
- вызов
bar()
<- еслиbar()
выдает исключение, объектTest
выделеноnew Test
не может быть удалено- вызов конструктора
std::shared_ptr<Test>
- вызов
foo()
Но в этом случае компилятор может знать, что есть утечка памяти. Компилятор не может сделать delete
автоматически, если выдается исключение?
Кроме того, компилятор делает автоматически delete
в таком случае:
Test *p = new Test;
это реализовано так:
- вызов
operator new
выделить память- вызов конструктора
Test
, Если конструктор выдает исключение, память автоматически удаляется.
Почему компилятор не работает в первом случае, в отличие от второго?
Компилятор обычно не может знать, что произошла утечка памяти.
Как только код возвращается из конструктора Test
,
Компилятор должен предположить, что объект был полностью и
правильно построено, что может (и часто так) означает, что
конструктор где-то зарегистрировал указатель на него, и
этот другой код будет ожидать, чтобы найти его.
Стандарт мог указали, что исключение вызовет
delete
вызываться на всех объектах, обновленных в полной версии
выражение, через которое оно проходит. Есть ряд исторических
причины, по которым это даже не рассматривалось. И сегодня было бы
возможно, сломать слишком много существующего кода.
Стандарт также мог бы наложить строгий порядок на
оценка выражения. Это уберет много проблем
неопределенного поведения, и сделать код более детерминированным (так
тесты более надежные). Исторически C не принимал это
маршрут из-за его влияния на оптимизацию; C ++ сохранил
править.