Когда я должен использовать новое ключевое слово в C++?
Я использую C++ в течение короткого времени, и мне было интересно о новая ключевое слово. Просто, я должен использовать его, или нет?
1) с новая ключевое слово...
MyClass* myClass = new MyClass();
myClass->MyField = "Hello world!";
2) Без новая ключевое слово...
MyClass myClass;
myClass.MyField = "Hello world!";
С точки зрения реализации, они не кажутся такими разными (но я уверен, что они есть)... Однако мой основной язык-C#, и, конечно же, 1-й метод-это то, что я использую к.
сложность заключается в том, что метод 1 сложнее использовать с классами std C++.
какой метод я должен использовать?
обновление 1:
Я недавно использовал новая ключевое слово кучу память (или бесплатная магазине) для большого массива, который выходит из области видимости (т. е. возвращается из функции). Где раньше я использовал стек, что привело к повреждению половины элементов вне области видимости, переключение на использование кучи гарантировало, что элементы были в такт. Ура!
обновление 2:
один друг недавно сказал мне, что есть простое правило для использования new ключевое слово; каждый раз, когда вы типа new, тип delete.
Foobar *foobar = new Foobar();
delete foobar; // TODO: Move this to the right place.
это помогает предотвратить утечки памяти, так как вы всегда должны поместить удаление где-то (т. е. когда вы вырезаете и вставляете его в деструктор или иным образом).
11 ответов:
метод 1 (с помощью
new)
- выделяет память для объекта на бесплатная магазине (это часто то же самое, что и кучу)
- требует, чтобы вы явно
deleteваш объект позже. (Если вы не удалите его, вы можете создать утечку памяти)- память остается выделенной, пока вы
deleteего. (т. е. вы могли быreturnобъект, созданный с помощьюnew)- пример в вопросе будет утечка если указатель
deleteD; и это всегда должны быть удалены, независимо от того, какой путь управления используется, или если возникают исключения.Метод 2 (не используя
new)
- выделяет память для объекта на стек (где все локальные переменные идут), как правило, меньше памяти доступно для стек; если вы выделяете слишком много объектов, вы рискуете переполнением стека.
- не нужно
deleteпозже.- память больше не выделяется, когда она выходит за рамки. (т. е. вы не должны
returnуказатель на объект в стеке)что касается того, какой из них использовать; вы выбираете метод, который лучше всего подходит для вас, учитывая вышеуказанные ограничения.
несколько простых случаев:
- если вы не хотите чтобы беспокоиться о вызове
delete(и потенциал, чтобы вызвать утечки памяти) вы не должны использоватьnew.- если вы хотите вернуть указатель на объект из функции, вы должны использовать
new
существует важное различие между ними.
все, что не выделено
newведет себя так же, как типы значений в C# (и люди часто говорят, что эти объекты выделяются в стеке, что, вероятно, является наиболее распространенным/очевидным случаем, но не всегда верно. Точнее, объекты, выделенные без использованияnewесть автоматическая длительность хранения Все, что выделено сnewвыделяется в куче и возвращается указатель на нее, точно так же, как ссылочные типы В C#.все, что выделяется в стеке, должно иметь постоянный размер, определенный во время компиляции (компилятор должен правильно установить указатель стека, или если объект является членом другого класса, он должен настроить размер этого другого класса). Вот почему массивы в C# являются ссылочными типами. Они должны быть, потому что со ссылочными типами, мы можем решить во время выполнения, сколько памяти нужно просить. И то же самое касается и здесь. Только массивы с постоянным размером (а размер, который может быть определен во время компиляции) может быть выделен с автоматической продолжительностью хранения (в стеке). Динамически размерные массивы должны быть выделены в куче, вызывая
new.(и вот где любое сходство с C# останавливается)
теперь все, что выделено в стеке, имеет" автоматическую " продолжительность хранения (вы можете фактически объявить переменную как
auto, но это значение по умолчанию, если не указан другой тип хранения, поэтому ключевое слово на самом деле не используется практика, но вот откуда она берется)автоматическая продолжительность хранения означает именно то, что это звучит, продолжительность переменной обрабатывается автоматически. Напротив, все, что выделено в куче, должно быть вручную удалено вами. Вот пример:
void foo() { bar b; bar* b2 = new bar(); }эта функция создает три значения, которые стоит учитывать:
в строке 1, он объявляет переменную
bтипаbarв стеке (автоматическая длительность).On строка 2, она объявляет
barуказательb2в стеке (автоматическая длительность),и вызывает new, выделяя abarобъект в куче. (динамическая длительность)когда функция вернется, произойдет следующее: Во-первых,
b2выходит за рамки (порядок разрушения всегда противоположен порядку строительства). Ноb2это просто указатель, поэтому ничего не происходит, память, которую он занимает, просто освобождается. И главное, память у него очки к (thebarэкземпляр в куче) не трогается. Освобождается только указатель, потому что только указатель имел автоматическую длительность. Во-вторых,bвыходит из области видимости, поэтому, поскольку он имеет автоматическую длительность, вызывается его деструктор, и память освобождается.и
barэкземпляр в куче? Вероятно, он все еще там. Никто не удосужился удалить его, поэтому мы просочились в память.из этого примера мы видим, что все, что имеет автоматическую длительность, является гарантированный, чтобы его деструктор вызывается, когда он выходит из области видимости. Это полезно. Но все, что выделяется в куче, длится столько, сколько нам нужно, и может быть динамически определено, как в случае массивов. Это тоже полезно. Мы можем использовать это для управления распределением памяти. Что делать, если класс Foo выделил некоторую память в куче в своем конструкторе и удалил эту память в своем деструкторе. Тогда мы могли бы получить лучшее из обоих миров, безопасный выделения памяти гарантированно будет освобожден снова, но без ограничений заставляя все быть на стеке.
и это в значительной степени точно, как работает большинство кода C++. Посмотрите на стандартную библиотеку
std::vectorнапример. Это обычно выделяется в стеке, но может быть динамически размером и размером. И он делает это, внутренне выделяя память в куче по мере необходимости. Пользователь класса никогда не видит этого, поэтому нет никаких шансов на утечку памяти или забывание очистить то, что вы выделили.этот принцип называется RAII (Resource Acquisition is Initialization), и он может быть распространен на любой ресурс, который должен быть приобретен и выпущен. (сетевые сокеты, файлы, подключения к базам данных, блокировки синхронизации). Все они могут быть приобретены в конструкторе и выпущены в деструкторе, поэтому вы гарантируете, что все ресурсы, которые вы приобретаете, снова освободятся.
как правило, никогда не используйте new / delete непосредственно с Вашего высокого уровня код. Всегда оборачивайте его в класс, который может управлять памятью для вас, и который гарантирует, что он снова освободится. (Да, из этого правила могут быть исключения. В частности, интеллектуальные указатели требуют от вас вызова
newнепосредственно, и передать указатель на его конструктор, который затем берет на себя и обеспечиваетdeleteназывается правильно. Но это все равно очень важное правило)
какой метод я должен использовать?
Это почти никогда не определяется вашими предпочтениями ввода, но контекстом. Если вам нужно сохранить объект через несколько стеков или если он слишком тяжел для стека, вы выделяете его в свободном магазине. Кроме того, поскольку вы выделяете объект, вы также несете ответственность за освобождение памяти. Поиск в
deleteоператора.чтобы облегчить бремя использования управления свободным магазином люди изобрели такие вещи, как
auto_ptrиunique_ptr. Я настоятельно рекомендую вам взглянуть на это. Они могут даже быть полезны для ваших вопросов ввода; -)
Если вы пишете на C++, вы, вероятно, пишете для производительности. Использование new и free store намного медленнее, чем использование стека (особенно при использовании потоков), поэтому используйте его только тогда, когда вам это нужно.
Как уже говорили другие, вам нужно новое, когда ваш объект должен жить за пределами функции или области объекта, объект действительно большой или когда вы не знаете размер массива во время компиляции.
кроме того, старайтесь никогда не использовать delete. Оберните новый в умный указатель вместо этого. Пусть интеллектуальный указатель вызовет delete для вас.
есть некоторые случаи, когда умный указатель не умный. Никогда не храните std:: auto_ptr внутри контейнера STL. Он будет удалять указатель слишком рано из-за операций копирования внутри контейнера. Другой случай, когда у вас есть действительно большой контейнер STL указателей на объекты. boost:: shared_ptr будет иметь тонну накладных расходов скорости, поскольку он натыкается на отсчет ссылок вверх и вниз. Лучший способ пойти в этом случае это поместить контейнер STL в другой объект и дать этому объекту деструктор, который вызовет delete для каждого указателя в контейнере.
короткий ответ: если вы новичок в C++, вы должны никогда использовать
newилиdeleteсебя. Вместо этого следует использовать интеллектуальные указатели, такие какstd::unique_ptr(или реже,std::shared_ptr). Таким образом, вам не придется так сильно беспокоиться об утечках памяти. И даже если вы более продвинутый, лучше всего было бы инкапсулировать пользовательский способ, который вы используетеnewиdeleteв небольшой класс (например, пользовательский смарт-указатель), который предназначен только для проблемы жизненного цикла объекта.конечно, за кулисами эти интеллектуальные указатели все еще выполняют динамическое выделение и освобождение, поэтому код, использующий их, все равно будет иметь связанные с ними накладные расходы во время выполнения. Другие ответы здесь рассмотрели эти вопросы и как принимать проектные решения о том, когда использовать интеллектуальные указатели, а не просто создавать объекты в стеке или включать их в качестве прямых членов объекта, достаточно хорошо, что я не буду их повторять. Но мое резюме было бы be: не используйте интеллектуальные указатели или динамическое распределение, пока что-то не заставит вас.
без
newключевое слово, которое вы храните на стек вызовов. Хранение чрезмерно больших переменных в стеке приведет к переполнение стека.
Если переменная используется только в контексте одной функции, вы лучше использовать переменную стека, т. е. Вариант 2. Как уже говорили другие, вам не нужно управлять временем жизни переменных стека - они создаются и разрушаются автоматически. Кроме того, выделение/освобождение переменной в куче происходит медленно по сравнению. Если функция вызывается достаточно часто, вы увидите огромное улучшение производительности, если использовать стек против кучи переменных переменная.
тем не менее, есть несколько очевидных случаев, когда переменные стека недостаточны.
Если переменная стека имеет большой объем памяти, то вы рискуете переполнить стек. По умолчанию размер стека каждого потока составляет 1 МБ на Windows. Маловероятно, что вы создадите переменную стека размером 1 МБ, но вы должны иметь в виду, что использование стека является кумулятивным. Если ваша функция вызывает функцию, которая вызывает другую функцию, которая вызывает другую функцию, которая..., переменные стека во всех этих функциях занимают место в одном стеке. Рекурсивные функции могут быстро столкнуться с этой проблемой, в зависимости от глубины рекурсии. Если это проблема, вы можете увеличить размер стека (не рекомендуется) или выделить переменную в куче с помощью оператора new (рекомендуем).
другое, более вероятное условие заключается в том, что ваша переменная должна "жить" за пределами области вашей функции. В этом случае вы бы выделили переменную в куче, чтобы она могла быть достигнута вне области действия любой заданной функции.
вы передаете myClass из функции или ожидаете, что он будет существовать вне этой функции? Как говорили некоторые другие, все дело в области, когда вы не выделяете в куче. Когда вы покидаете функцию, она уходит (в конце концов). Одной из классических ошибок начинающих является попытка создать локальный объект некоторого класса в функции и вернуть его, не выделяя его в куче. Я помню, как отлаживал такие вещи еще в мои ранние дни, делая c++.
простой ответ-да-new () создает объект в куче (с неудачным побочным эффектом, который вы должны управлять его временем жизни (явно вызывая delete на нем), тогда как вторая форма создает объект в стеке в текущей области, и этот объект будет уничтожен, когда он выйдет из области.
короткий ответ-да ключевое слово " new " невероятно важно, так как при его использовании данные объекта хранятся в куче, а не в стеке, что наиболее важно!
второй метод создает экземпляр в стеке вместе с такими вещами, как что-то объявленное
intи список параметров, которые передаются в функцию.первый метод освобождает место для указатель в стеке, который вы установили в место в памяти, где новый
MyClassбыл выделен в куче или свободном хранилище.первый метод также требует, чтобы вы
deleteто, что вы создаете сnew, тогда как во втором метод, класс автоматически разрушается и освобождается, когда он выпадает из области видимости (следующая закрывающая скобка, как правило).
Comments