LINUX.ORG.RU

Вопрос с собеседования.

 


2

2

Вопрос звучал так: почему в этом коде течёт память. Может троллили и она не течёт, но когда я ответил, они радостно приняли ответ, показав что его и ждали и поехали дальше.

T() исключений не кидает, память внутри не выделяет.
f() память внутри не выделяет, возвращает int, но может кинуть исключение.

std::shared_ptr<T> t(new T( f() ) );
Я ответил такой бред: перед конструкированием объекта (вызовом T()) должна быть выделена память. Тогда возможна последовательность: malloc (от new), f(), T(). Если f() кинуло исключение, то выделенная память, в которой должен конструироваться объект, никем не освободится. Но это лютый бред, поскольку, вроде как если в ходе new летит исключение, то память, которую сам оператор new выделил, гарантированно освободится обратно. Да, исключение полетело не из конструктора T(), а при вычислении аргументов для передачи в конструктор, но я всегда думал что в случае оператора new это всё то же самое, как если бы полетело из конструктора.

Короче, я стал сомневаться в том, что собеседующий был вменяем. Течёт скорее всего у него где-то в другом месте.



Последнее исправление: hlamotron (всего исправлений: 2)

Вызов f() происходит до вызова operator new() так что утечь ничего не может

anonymous
()

If any part of the object initialization described above terminates by throwing an exception, storage has
been obtained for the object, and a suitable deallocation function can be found, the deallocation function is
called to free the memory in which the object was being constructed, after which the exception continues to
propagate in the context of the new-expression

Gvidon ★★★★
()
Последнее исправление: Gvidon (всего исправлений: 1)
Ответ на: комментарий от Gvidon

Цитата оборвалась на самом интересном месте, дальше же прямо ответ на вопрос идёт:

... If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object’s memory to be freed. [ Note: This is appropriate when the called allocation function does not allocate memory; otherwise, it is likely to result in a memory leak. — end note ]

xaizek ★★★★★
()
Ответ на: комментарий от Gvidon

Помнится, ранее речь шла не о таких случаях как в стартовом сообщении, а о выражениях вида:

void demo(unique_ptr<T> a, shared_ptr<X> b){...}
...
demo(unique_ptr<T>( new T(f()) ), shared_ptr<X>( new X(f(), g(), h()) ));
И фокус состоял в том, что компилятор не обязательно должен вызвать конструктор unique_ptr сразу после того, как выполнит new T(). Поскольку порядок вычисления аргументов не определен, то компилятор может вызвать new T, затем new X, только затем конструкторы unique_ptr и shared_ptr. Поэтому если после new T возникнет исключение в конструкторе X, то произойдет утечка памяти.

А вот что именно интервьюеры нашли в примере из стартового сообщения — это интересно.

eao197 ★★★★★
()

Из-за исключения в f() утечки не будет.

Что имел ввиду ревьюер — без понятия.

b0r3d0m
()

не будет тут утечки. Если только не убрать деалокатор умышленно.

будет если 2 таких параметра будет передаваться. Только порядок вызова функций определит конпелятор и может вызвать сначала все new, а потом уже конструкторы умных указателей. Есть подозрение, что интревьюйеры просто урезали пример с 2 параметрами и выё..ваются.

anonymous
()
Ответ на: комментарий от eao197

а вот. именно эту ситуёовину и имел ввиду выше, да.

anonymous
()

По идее, происходит такая последовательность вызовов:

  • Выделяется память
  • Вычисляются аргументы конструктора (порядок не определён, но точно до вызова конструктора)
  • Вызывается конструктор

Если конструктор кидает исключение, то память освободится. А вот считается ли вычисление аргументов, частью вызова конструктора? Физически на всех распостраннёных системах это точно не так. А что говорит по этому поводу стандарт - я не знаю.

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

Выделяется память

Кем выделяется?

при вызове new должен уже быть вычислен его аргумент. То есть если он (f()) кинет исключение, то память не будет освобождена, потому что не будет выделена.

далее исключение может кинуть оператор копирования того, что возвращает f( ), но и в этом случае память будет освобождена.

потом может кинуть исключение конструктор. собссно тут все понятно.

anonymous
()
Ответ на: комментарий от anonymous

Ещё скажи, что вызов конструктора происходит до выделения памяти.

Но про то, что any part of... я уже успел нагуглить. Но тут тупо надо знать стандарт в этой части.

pon4ik ★★★★★
()
Ответ на: комментарий от anonymous

Более того, не определенно что случится раньше вызов f() или выделение памяти. Но они оба случаться точно раньше вызова конструктора. Ну это как бы и логично и в духе плюсов :)

pon4ik ★★★★★
()
Ответ на: комментарий от hlamotron

Может тупо троллили и правильным тут считается доказать, что утечки нет?

Запросто могут сказать, что правильный ответ не верный. Просто чтобы посмотреть, как ты будешь доказывать свою правоту.

i-rinat ★★★★★
()

откуда вы все берётесь?

видимо, моя теория о том, что около 80% биологических организмов, получающих деньги за код на крестах, пишут его не приходя в сознание, верна. вопрос разбирается в 2 этапа:

1. надо понимать разницу между выражением new и оператором new:

A new-expression may obtain storage for the object by calling an allocation function (3.7.4.1). If the
new-expression terminates by throwing an exception, it may release storage by calling a deallocation function
(3.7.4.2).
в выражении new T( f() ) утечки нет.

2. как работает конструктор shared_ptr. в частности то, что он вызывает ещё одно выделение памяти:

6 Throws: bad_alloc, or an implementation-defined exception when a resource other than memory
could not be obtained.
7 Exception safety: If an exception is thrown, delete p is called.
и здесь утечки нет.

anonymous
()
Ответ на: комментарий от dt1

это потому что «may obtain». да, писатели стандартов тоже имеют право на косноязычность.

anonymous
()
Ответ на: комментарий от anonymous

там все правила разрешённого порядка вычисления statements. в том числе есть ответ, что утечки памяти в примере нет.

dzidzitop ★★
()
Ответ на: комментарий от dzidzitop

т.е. набросил не разобравшись

какое отношение порядок вычисления имеет к осбенностям поведения выражения new или конструктора shared_ptr?

это?

12) The call to the allocation function (operator new) is indeterminately sequenced with respect to (until C++17)sequenced before (since C++17) the evaluation of the constructor arguments in a new-expression
здесь написано «sequenced before (since C++17)», это значит сначала выделить память, а потом вызвать f(). ещё написано «indeterminately sequenced ... (until C++17)», т.е. вообще как попало.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.