Правила интеллектуального указателя и правила выбора списка параметров
Страница MSDN об интеллектуальных указателях включает в себя повышенное предупреждение о создании интеллектуальных указателей в списках параметров:
Всегда создавайте интеллектуальные указатели на отдельной строке кода, никогда в списке параметров, так что тонкая утечка ресурсов не возникает из-за определенных правил распределения списков параметров.
Каковы правила распределения списков параметров, на которые он ссылается? При каких обстоятельствах может произойти утечка ресурса?
Ответы
Ответ 1
Это относится к возможности оценки параметров в другом порядке, например
func(unique_ptr<MyClass>(new MyClass), a(), b());
если порядок оценки: a()
, MyClass()
, b()
, то построено unique_ptr
, может случиться, что будут выброшены выбросы b()
и память.
Защита (которая была добавлена в С++ 14, а также более эффективная) заключается в использовании make_unique
(при условии, что MSVC и в соответствии с вашей версией компилятора вам может потребоваться определить один самостоятельно или см. здесь). То же самое относится к shared_ptr
.
Посмотрите на заметки для std:: make_shared здесь:
Кроме того, код, такой как f(std::shared_ptr<int>(new int(42)), g())
, может вызывают утечку памяти, если g
выдает исключение, поскольку g()
можно вызвать после new int(42)
и перед конструктором shared_ptr<int>
. Эта не встречается в f(std::make_shared<int>(42), g())
, так как две функции вызовы никогда не чередуются.
Ответ 2
Если вы это сделаете:
func(shared_ptr<Foo>(new Foo), shared_ptr<Bar>(new Bar));
И подпись:
void func(shared_ptr<Foo>, shared_ptr<Bar>);
Что произойдет, если один из конструкторов выбрасывает? Может случиться так, что new
был вызван один раз успешно, а затем другой сбой (вы не знаете, какой из них будет вызван первым). Если это произойдет, один из объектов может быть просочился, потому что он никогда не удерживался менеджером ресурсов.
Вы можете прочитать больше здесь: http://www.gotw.ca/gotw/056.htm
Ответ 3
Проблема в том, что если у вас есть функция, которая принимает несколько аргументов:
void func( const std::shared_ptr< MyFirstClass >& ptr, const MySecondClass& ref );
и вы вызываете эту функцию следующим образом:
func( std::shared_ptr< MyFirstClass >( new MyFirstClass ), MySecondClass() );
компилятор может свободно выполнять выражения в списке аргументов в любом порядке. Некоторые из этих заказов могут быть проблематичными. Например, представьте, что компилятор решает выполнить
new MyFirstClass
а затем
MySecondClass()
и, наконец, c'tor из std:: shared_ptr <MyFirstClass> (передавая ему адрес экземпляра MyFirstClass, который был выделен в бесплатном хранилище на первом шаге).
Пока все хорошо. Но если второй шаг выдает исключение, то shared_ptr никогда не будет создан, а ваш свободный магазин, выделенный MyFirstClass-экземпляру, навсегда потерян.