Когда следует использовать ключевое слово new в 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)

Метод 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, выделяя bar объект в куче. (динамическая длительность)

когда функция вернется, произойдет следующее: Во-первых, b2 выходит за рамки (порядок уничтожения всегда противоположен порядку строительства). Но!--9--> это просто указатель, поэтому ничего не происходит, память, которую он занимает, просто освобождается. А главное, память у него очки к (the bar экземпляр в куче) не трогается. Освобождается только указатель, поскольку только указатель имеет автоматическую длительность. Во-вторых, b выходит за рамки, поэтому, поскольку он имеет автоматическую длительность, вызывается его деструктор, и память освобождается.

и barэкземпляр в куче? Это, наверное, все-таки есть. Никто не удосужился удалить его, так что у нас утечка памяти.

из этого примера мы видим, что все, что имеет автоматическую продолжительность, гарантированный, чтобы его деструктор вызывается, когда он выходит из области видимости. Это полезно. Но все, что выделяется в куче, длится столько, сколько нам нужно, и может быть динамически определено, как в случае массивов. Это тоже полезно. Мы можем использовать это для управления распределением памяти. Что делать, если класс Foo выделил некоторую память в куче в своем конструкторе и удалил эту память в своем деструкторе. Тогда мы могли бы получить лучшее из обоих миров, безопасный выделения памяти гарантированно будет освобожден снова, но без ограничений заставляя все быть в стеке.

и это в значительной степени именно то, как работает большинство кода C++. Посмотрите на стандартную библиотеку std::vector например. Это обычно выделяется в стеке,но может быть динамически изменено и изменено. И он делает это, внутренне выделяя память в куче по мере необходимости. Пользователь класса никогда не видит этого, поэтому нет никаких шансов на утечку памяти или забывание очистить то, что вы выделили.

этот принцип называется RAII (приобретение ресурсов-Инициализация), и он может быть распространен на любой ресурс, который должен быть приобретен и выпущен. (сетевые сокеты, файлы, подключения к базе данных, блокировки синхронизации). Все они могут быть приобретены в конструкторе и выпущены в деструкторе, поэтому вы гарантируете, что все ресурсы, которые вы приобретете, снова освободятся.

как правило, никогда не используйте new / delete непосредственно с Вашего высокого уровня код. Всегда оборачивайте его в класс, который может управлять памятью для вас и который гарантирует, что он снова освободится. (Да, из этого правила могут быть исключения. В частности, смарт-указатели требуют, чтобы вы позвонили new напрямую, и передать указатель на конструктор, который берет на себя и гарантирует,delete называется правильно. Но это все еще очень важное эмпирическое правило)


какой метод я должен использовать?

Это почти никогда не определяется вашими предпочтениями ввода, но контекстом. Если вам нужно сохранить объект через несколько стеков или если он слишком тяжелый для стека, вы выделяете его в free store. Кроме того, поскольку вы выделяете объект, вы также отвечаете за освобождение памяти. Поиск delete оператора.

чтобы облегчить бремя использования управления свободным магазином, люди изобрели такие вещи, как auto_ptr и unique_ptr. Я настоятельно рекомендую вам взглянуть на это. Они могут даже помочь в ваших проблемах с вводом текста; -)


Если вы пишете на C++, вы, вероятно, пишете для производительности. Использование new и free store намного медленнее, чем использование стека (особенно при использовании потоков), поэтому используйте его только тогда, когда вам это нужно.

Как говорили другие, вам нужно новое, когда ваш объект должен жить за пределами функции или области объекта, объект действительно большой или когда вы не знаете размер массива во время компиляции.

кроме того, старайтесь избегать использования delete. Оберните новый в вместо этого smart pointer. Пусть вызов smart pointer 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, тогда как во втором метод, класс автоматически разрушается и освобождается, когда он выпадает из области видимости (обычно следующая закрывающая скобка).