Изучая разные языки, я часто видел объекты, выделяемые на лету, чаще всего в Java и C#, например:
functionCall(new className(initializers));
Я понимаю, что это совершенно законно для языков с управлением памятью, но можно ли использовать этот метод в C++, не вызывая утечки памяти?





Да, если вы освобождаете память внутри функции. Но это ни в коем случае не лучшая практика для C++.
Выделить, вызвать функцию, освободить. Дополнительная переменная, необходимая для указания на выделенный объект.
Как обычно, лучшая практика - RAII. Если у вас есть вызовы new, видимые в вашем пользовательском коде, велика вероятность, что вы делаете это неправильно. По крайней мере, оберните выделение в умный указатель. Я просто добавил ответ, демонстрирующий это.
Если U может изменить код функции, поместите туда новый и передайте инициализаторы. Сохраняйте переменные как можно более локальными. Если у U нет доступа к коду, это БУДЕТ вызывать утечку памяти. Создайте указатель на новый объект, на одну строку вверх и передайте его ..
Кстати ... Это лучше всего и в C#, функции в вызовах функций всегда плохая идея для отладки. По одной идее в строке. :)
По-разному.
Это передает "владение" памятью functionCAll (). Ему нужно будет либо освободить объект, либо сохранить указатель, чтобы его можно было освободить позже. Подобная передача прав собственности на необработанные указатели - один из самых простых способов встроить в код проблемы с памятью - либо утечки, либо двойные удаления.
И передача права собственности, как это, обычно является грязной сделкой, потому что это не очевидно, если взглянуть на необработанный указатель, которому он принадлежит.
Это будет работать для объектов, созданных в стеке, но не для обычного указателя в C++.
Может быть, автоматический указатель справится с этим, но я еще не слишком много с ними связывал, чтобы знать.
Обычный auto_ptr не будет (сам вызов функции создает копию auto_ptr, что приводит к передаче права собственности), но const auto_ptr должен (если я правильно понял книгу Джозаттиса) делать то, что вы ожидаете.
В общем, нет, если только вы не хочу для утечки памяти. На самом деле, в большинстве случаев это не сработает, поскольку результат
new T();
в C++ - это T *, а не T (в C# new T () возвращает T).
Взгляните на Умные указатели или Сборщик мусора для C и C++.
Это безопасно, если вызываемая функция имеет семантику принятия права собственности. Я не помню, когда мне это было нужно, поэтому считаю это необычным.
Если функция работает таким образом, она должна принимать свой аргумент как объект интеллектуального указателя, чтобы намерение было чистым; т.е.
void functionCall(std::auto_ptr<className> ptr);
скорее, чем
void functionCall(className* ptr);
Это делает передачу права собственности явной, и вызывающая функция будет избавляться от памяти, на которую указывает ptr, когда выполнение функции выходит за рамки.
Ваш код действителен (при условии, что functionCall () действительно гарантирует, что указатель будет удален), но он хрупкий и вызовет тревогу в головах большинства программистов на C++.
С вашим кодом есть несколько проблем:
functionCall(new className(initializers), new className(initializers)); Представьте, что первый выделен успешно, но второй выдает исключение (возможно, не хватает памяти, или, может быть, конструктор класса сгенерировал исключение). Тогда functionCall никогда не вызывается, а не могу освобождает память.Простое (но все же беспорядочное) решение - сначала выделить память и сохранить указатель, а затем освободить его в той же области, в которой он был объявлен (так что вызывающая функция владеет памятью):
className* p = new className(initializers);
functionCall(p);
delete p;
Но это по-прежнему беспорядок. Что, если functionCall выдаст исключение? Тогда p не будет удален. Если мы не добавим попытку / уловку во всем, но черт возьми, это грязно. Что, если функция станет немного сложнее и может вернуться после functionCall, но до удаления? Упс, утечка памяти. Невозможно поддерживать. Плохой код.
Итак, одно из хороших решений - использовать умный указатель:
boost::shared_ptr<className> p = boost::shared_ptr<className>(new className(initializers));
functionCall(p);
Теперь вопрос о владении памятью. shared_ptr владеет памятью и гарантирует, что она будет освобождена. Конечно, мы могли бы использовать std::auto_ptr, но shared_ptr реализует семантику, которую вы обычно ожидаете.
Обратите внимание, что я по-прежнему выделил память в отдельной строке, потому что проблема с несколькими выделениями в одной строке при вызове функции все еще существует. Один из них еще может выкинуть, значит, у вас утечка памяти.
Умные указатели - это, как правило, абсолютный минимум, необходимый для управления памятью. Но часто решение красивый состоит в том, чтобы написать свой собственный класс RAII.
className должен быть размещен в стеке, и в его конструкторе укажите, какие распределения с new необходимы. И в своем деструкторе он должен освободить эту память. Таким образом, вы гарантируете, что утечки памяти не произойдет, и вы можете сделать вызов функции таким простым:
functionCall(className(initializers));
Стандартная библиотека C++ работает так. std::vector - один из примеров. Вы бы никогда не разместили вектор с помощью new. Вы размещаете его в стеке, и позволяете ему заниматься распределением памяти внутри.
Избегайте создания нулевого ptr и его присвоения, вы не гарантируете, что T p = T (x) будет скомпилирован оптимизированным как T p (x); т.е. boost :: shared_ptr <className> p (новое имя класса (инициализаторы)); В этом конкретном случае вы также можете рассматривать std :: auto_ptr как безопасный для памяти, без необходимости совместного использования.
В C++ мы бы не создавали память таким образом динамически. Вместо этого вы должны создать временный объект стека.
Вам нужно только создать объект кучи через new, если вы хотите, чтобы время жизни объекта было больше, чем время вызова функции. В этом случае вы можете использовать new в сочетании с интеллектуальным указателем (см. Другие ответы для примера).
// No need for new or memory management just do this
functionCall(className(initializers));
// This assumes you can change the functionCall to somthing like this.
functionCall(className const& param)
{
<< Do Stuff >>
}
Если вы хотите передать неконстантную ссылку, сделайте это так:
calssName tmp(initializers);
functionCall(tmp);
functionCall(className& param)
{
<< Do Stuff >>
}
Тогда что было бы лучше?