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