Почему программисты на C ++ должны минимизировать использование «нового»?

755

Я наткнулся на вопрос "Переполнение стека". Утечка памяти с помощью std :: string при использовании std :: list <std :: string>, и один из комментариев говорит об этом:

Прекратите использовать new так много. Я не вижу причины, по которой вы использовали новое, где бы вы ни были. Вы можете создавать объекты по значению в C++ и это одно из огромных преимуществ использования языка. Вам не нужно выделять все в кучу. Прекратите думать, как программист на Java.

Я не совсем уверен, что он имеет в виду. Почему объекты должны быть созданы по значению в C++ настолько часто, насколько это возможно, и какая разница делает его внутренне? Я неправильно понял ответ?

Теги:
memory-management
heap
new-operator
c++-faq

18 ответов

950
Лучший ответ

Существует два широко используемых метода выделения памяти: автоматическое распределение и динамическое распределение. Обычно для каждой области памяти имеется соответствующая область памяти: стек и куча.

Stack

Стек всегда выделяет память последовательным образом. Он может сделать это, потому что он требует освобождения памяти в обратном порядке (First-In, Last-Out: FILO). Это метод распределения памяти для локальных переменных на многих языках программирования. Это очень, очень быстро, потому что он требует минимальной бухгалтерской отчетности, а следующий адрес для выделения является неявным.

В С++ это называется автоматическим хранилищем, поскольку хранилище автоматически заявляется в конце области. Как только выполняется выполнение текущего блока кода (с разделителем с помощью {}), память для всех переменных в этом блоке будет автоматически собрана. Это также момент, когда деструкторы вызываются для очистки ресурсов.

Heap

Куча позволяет использовать более гибкий режим выделения памяти. Бухгалтерия является более сложной и распределение происходит медленнее. Поскольку не существует неявной точки выпуска, вы должны освободить память вручную, используя delete или delete[] (free в C). Однако отсутствие неявной точки выпуска является ключом к гибкости кучи.

Причины использования динамического распределения

Даже если использование кучи происходит медленнее и потенциально приводит к утечкам памяти или фрагментации памяти, для динамического распределения есть очень хорошие варианты использования, поскольку они менее ограничены.

Две основные причины использования динамического распределения:

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

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

Почему динамическое распределение часто не требуется

В С++ существует аккуратная конструкция, называемая деструктором. Этот механизм позволяет вам управлять ресурсами путем согласования ресурса ресурса со временем жизни переменной. Этот метод называется RAII и является отличительной чертой С++. Он "обертывает" ресурсы на объекты. std::string - прекрасный пример. Этот фрагмент:

int main ( int argc, char* argv[] )
{
    std::string program(argv[0]);
}

фактически выделяет переменный объем памяти. Объект std::string выделяет память с помощью кучи и освобождает ее в своем деструкторе. В этом случае вам не нужно было вручную управлять любыми ресурсами и получать преимущества динамического выделения памяти.

В частности, это означает, что в этом фрагменте:

int main ( int argc, char* argv[] )
{
    std::string * program = new std::string(argv[0]);  // Bad!
    delete program;
}

имеется ненужное распределение динамической памяти. Программа требует больше ввода (!) И вводит риск забыть освободить память. Он делает это без видимой пользы.

Почему вы должны использовать автоматическое хранилище как можно чаще

В принципе, последний абзац подводит итог. Использование автоматического хранилища как можно чаще делает ваши программы:

  • быстрее напечатать;
  • быстрее при запуске;
  • менее подвержен утечкам памяти/ресурсов.

Бонусные очки

В указанном вопросе возникают дополнительные проблемы. В частности, следующий класс:

class Line {
public:
    Line();
    ~Line();
    std::string* mString;
};

Line::Line() {
    mString = new std::string("foo_bar");
}

Line::~Line() {
    delete mString;
}

На самом деле гораздо более рискованно использовать, чем следующий:

class Line {
public:
    Line();
    std::string mString;
};

Line::Line() {
    mString = "foo_bar";
    // note: there is a cleaner way to write this.
}

Причина в том, что std::string правильно определяет конструктор копирования. Рассмотрим следующую программу:

int main ()
{
    Line l1;
    Line l2 = l1;
}

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

Другие примечания

Широкое использование RAII считается лучшей практикой на С++ из-за всех вышеперечисленных причин. Однако есть дополнительное преимущество, которое не сразу становится очевидным. В принципе, это лучше, чем сумма его частей. Составлен весь механизм. Он масштабируется.

Если вы используете класс Line как строительный блок:

 class Table
 {
      Line borders[4];
 };

Тогда

 int main ()
 {
     Table table;
 }

выделяет четыре экземпляра std::string, четыре экземпляра Line, один экземпляр Table и все содержимое строки, и все автоматически освобождается.

  • 42
    +1 за упоминание RAII в конце, но должно быть что-то об исключениях и раскручивании стека.
  • 4
    @Tobu: да, но этот пост уже довольно длинный, и я хотел бы сосредоточиться на вопросе OP. Я в конечном итоге напишу сообщение в блоге или что-то и ссылку на него отсюда
Показать ещё 10 комментариев
160

Поскольку стек быстрый и надежный

В С++ для каждой локальной области видимости в заданной функции требуется только одна команда для выделения пространства - в стеке, и невозможно утечка какой-либо из этой памяти. Этот комментарий предполагал (или должен был предполагать) сказать что-то вроде "использовать стек, а не кучу".

  • 4
    За исключением того, что это не так. Посмотрите пример, который я написал ниже - вы не можете распределить состояние класса внутри класса в стеке; совершите ошибку, и стек вам не поможет.
  • 17
    «требуется только одна инструкция для выделения места» - о, бред. Конечно, для добавления к указателю стека требуется всего одна инструкция, но если у класса есть какая-либо интересная внутренняя структура, будет гораздо больше, чем просто добавление к указателю стека. В равной степени справедливо сказать, что в Java не требуется никаких инструкций для выделения пространства, потому что компилятор будет управлять ссылками во время компиляции.
Показать ещё 13 комментариев
96

Это сложно.

Во-первых, С++ не собирает мусор. Поэтому для каждого нового должно быть соответствующее удаление. Если вы не введете это удаление, тогда у вас будет утечка памяти. Теперь для простого случая:

std::string *someString = new std::string(...);
//Do stuff
delete someString;

Это просто. Но что произойдет, если "Do stuff" выдает исключение? Упс: утечка памяти. Что произойдет, если "Делать вещи" выдает return раньше? К сожалению, утечка памяти.

И это для простейшего случая. Если вам удастся вернуть эту строку кому-то, теперь они должны ее удалить. И если они передают это в качестве аргумента, должен ли человек, получающий его, удалить его? Когда они должны удалить его?

Или вы можете просто сделать это:

std::string someString(...);
//Do stuff

Нет delete. Объект был создан в "стеке", и он будет уничтожен после того, как он выйдет за рамки. Вы даже можете вернуть объект, передавая его содержимое вызывающей функции. Вы можете передать объект в функции (как правило, в качестве ссылки или const-ссылки: void SomeFunc(std::string &iCanModifyThis, const std::string &iCantModifyThis) и т.д.

Все без new и delete. Там нет вопроса о том, кто владеет памятью или кто несет ответственность за ее удаление. Если вы выполните:

std::string someString(...);
std::string otherString;
otherString = someString;

Понятно, что otherString имеет копию данных someString. Это не указатель; это отдельный объект. У них может быть одно и то же содержимое, но вы можете изменить его, не затрагивая другое:

someString += "More text.";
if(otherString == someString) { /*Will never get here */ }

Посмотрите идею?

  • 0
    На этом примечании ... Если объект динамически размещается в main() , существует на время выполнения программы, не может быть легко создан в стеке из-за ситуации, и указатели на него передаются любым функциям, которые требуют доступ к нему, может ли это привести к утечке в случае сбоя программы, или это будет безопасно? Я бы предположил последнее, поскольку операционная система, освобождающая всю память программы, должна также логически освобождать ее, но я не хочу ничего предполагать, когда дело касается new .
  • 3
    @JustinTime Вам не нужно беспокоиться об освобождении памяти динамически размещаемых объектов, которые должны оставаться в течение всей жизни программы. Когда программа выполняется, ОС создает для нее атлас физической памяти или виртуальной памяти. Каждый адрес в пространстве виртуальной памяти сопоставляется с адресом физической памяти, и при выходе из программы все, что отображается в его виртуальной памяти, освобождается. Таким образом, пока программа полностью завершается, вам не нужно беспокоиться о том, что выделенная память никогда не будет удалена.
72

Объекты, созданные new, должны быть в конечном итоге delete d, чтобы они не протекали. Деструктор не будет вызван, память не будет освобождена, весь бит. Поскольку С++ не содержит сборку мусора, это проблема.

Объекты, созданные по значению (то есть на стеке), автоматически умирают, когда они выходят за рамки. Деструкторный вызов вставляется компилятором, и память автоматически освобождается после возврата функции.

Умные указатели, такие как auto_ptr, shared_ptr, решают проблему с обвисшей ссылкой, но им нужна дисциплина кодирования и есть другие проблемы (копируемость, циклы ссылок и т.д.).

Кроме того, в сценариях с большой степенью многопоточности new является точкой раздора между потоками; может возникнуть влияние производительности на чрезмерное использование new. Создание объекта стека по определению является поточно-локальным, поскольку каждый поток имеет свой собственный стек.

Недостатком объектов значений является то, что они умирают после возвращения функции хоста - вы не можете передавать ссылку на тех, кто возвращается к вызывающему, только путем копирования или возврата по значению.

  • 8
    +1. Re "Объекты, созданные new должны быть в конечном итоге delete чтобы они не просочились". - что еще хуже, new[] должен соответствовать delete[] , и вы получите неопределенное поведение, если delete new[] -ed память или delete[] new -ed memory - очень немногие компиляторы предупреждают об этом (некоторые инструменты, такие как Cppcheck, делают когда они могут).
  • 2
    @TonyDelroy Бывают ситуации, когда компилятор не может этого предупредить. Если функция возвращает указатель, она может быть создана, если new (единственный элемент) или new [].
27
  • С++ не использует менеджер памяти самостоятельно. Другие языки, такие как С#, Java, имеют сборщик мусора для обработки памяти.
  • С++ с помощью подпрограмм операционной системы для выделения памяти и слишком много нового/удаления может фрагментировать доступную память
  • В любом приложении, если часто используется память, рекомендуется предварительно выделить его и отпустить, когда это не требуется.
  • Неправильное управление памятью может привести к утечкам памяти, и это очень сложно отслеживать. Таким образом, использование объектов стека в рамках функции является проверенной техникой.
  • Недостатком использования объектов стека является создание нескольких копий объектов при возврате, передача функций и т.д. Однако интеллектуальные компиляторы хорошо знают об этих ситуациях, и они хорошо оптимизированы для производительности
  • Это действительно утомительно в С++, если память выделяется и выпускается в двух разных местах. Ответственность за выпуск всегда является вопросом, и в основном мы полагаемся на некоторые общедоступные указатели, объекты стека (максимально возможные) и такие методы, как auto_ptr (объекты RAII)
  • Лучше всего, что вы контролируете память, а самое худшее - у вас не будет никакого контроля над памятью, если мы будем использовать неправильное управление памятью для приложения. Аварии, вызванные повреждением памяти, являются самыми отвратительными и трудными для отслеживания.
  • 3
    На самом деле, любой язык, который выделяет память, имеет менеджер памяти, включая c. Большинство просто очень просто, то есть int * x = malloc (4); int * y = malloc (4); ... первый вызов будет выделять память, или, как правило, запрашивать у памяти (обычно в блоках 1k / 4k), так что второй вызов фактически не выделяет память, а дает вам часть последнего блока, для которого он выделен. IMO, сборщики мусора не являются менеджерами памяти, потому что они обрабатывают только автоматическое освобождение памяти. Чтобы называться диспетчером памяти, он должен не только обрабатывать освобождение, но и выделять память.
  • 0
    Локальные переменные используют стек, поэтому компилятор не отправляет вызов malloc() или его друзьям для выделения необходимой памяти. Однако стек не может освободить какой-либо элемент в стеке, единственный способ, которым когда-либо освобождается память стека, - это раскручивание с вершины стека.
18

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

Оператор new имеет недетерминированное время выполнения

Вызов new может или не может заставить ОС выделять новую физическую страницу для вашего процесса, это может быть довольно медленным, если вы часто это делаете. Или у этого уже может быть подходящее место памяти, мы не знаем. Если ваша программа должна иметь согласованное и прогнозируемое время выполнения (например, в режиме реального времени или в игре/физическом моделировании), вам нужно избегать new в ваших критических циклах времени.

Оператор new - это неявная синхронизация потоков

Да, вы меня слышали, ваша ОС должна убедиться, что ваши таблицы страниц согласованы, и поскольку такой вызов new приведет к тому, что ваш поток получит неявный блокировку мьютекса. Если вы последовательно вызываете new из многих потоков, вы фактически сериализуете свои потоки (я сделал это с 32 процессорами, каждый из которых попадал на new, чтобы получить несколько сотен байт каждый, ох! Это была королевская лаваша для отладки )

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

  • 0
    И того, и другого можно избежать, используя размещение new / delete и выделение памяти заранее. Или вы можете выделить / освободить память самостоятельно, а затем вызвать конструктор / деструктор. Так обычно работает std :: vector.
  • 0
    @ rxantos Пожалуйста, прочитайте OP, этот вопрос о том, как избежать ненужного выделения памяти. Кроме того, нет размещения удалить.
Показать ещё 3 комментария
17

В значительной степени тот, кто поднимает свои слабости до общего правила. Там нет ничего плохого в создании объектов с помощью оператора new. Для некоторых есть аргумент в том, что вы должны сделать это с некоторой дисциплиной: если вы создаете объект, вам нужно убедиться, что он будет уничтожен.

Самый простой способ сделать это - создать объект в автоматическом хранилище, поэтому С++ знает, как его уничтожить, когда он выходит из области видимости:

 {
    File foo = File("foo.dat");

    // do things

 }

Теперь заметите, что, когда вы отвалитесь от этого блока после конечной скобки, foo выходит за рамки. С++ вызовет его dtor автоматически для вас. В отличие от Java, вам не нужно ждать, пока GC ее не найдет.

Если бы вы написали

 {
     File * foo = new File("foo.dat");

вы хотите явно сопоставить его с

     delete foo;
  }

или даже лучше, выделите File * как "умный указатель". Если вы не будете осторожны, это может привести к утечкам.

Сам ответ ошибочно полагает, что если вы не используете new, вы не выделяете кучу; на самом деле, на С++ вы этого не знаете. В лучшем случае вы знаете, что небольшое количество памяти, скажем, одного указателя, конечно же выделяется в стеке. Однако рассмотрим, является ли реализация файла чем-то вроде

  class File {
    private:
      FileImpl * fd;
    public:
      File(String fn){ fd = new FileImpl(fn);}

то FileImpl по-прежнему будет выделено в стеке.

И да, вы должны быть уверены, что

     ~File(){ delete fd ; }

в классе; без него вы будете утечка памяти из кучи, даже если вы явно не выделили кучу вообще.

  • 4
    Вы должны взглянуть на код в указанном вопросе. В этом коде определенно много чего не так.
  • 7
    Я согласен, что нет ничего плохого в использовании new как такового , но если вы посмотрите на оригинальный код, на который ссылался комментарий, то new злоупотребляют. Код написан так, как будто это был Java или C #, где new используется практически для каждой переменной, когда в стеке гораздо больше смысла.
Показать ещё 12 комментариев
16

Pre-С++ 17:

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

Рассмотрим "осторожного" пользователя, который не забывает оборачивать объекты в умные указатели:

foo(shared_ptr<T1>(new T1()), shared_ptr<T2>(new T2()));

Этот код опасен, потому что нет никакой гарантии, что shared_ptr до T1 или T2. Следовательно, если один из new T1() или new T2() завершится неудачно после того, как другой преуспеет, то первый объект будет пропущен, потому что не существует shared_ptr для его уничтожения и освобождения.

Решение: используйте make_shared.

Post-С++ 17:

Это больше не проблема: С++ 17 налагает ограничение на порядок этих операций, в этом случае гарантируя, что за каждым вызовом new() должен немедленно следовать построение соответствующего умного указателя, без других операций в между. Это подразумевает, что ко времени вызова второго new() гарантируется, что первый объект уже обернут в его умный указатель, таким образом предотвращая любые утечки в случае возникновения исключения.

Более подробное объяснение нового порядка оценки, введенного С++ 17, было предоставлено Барри в другом ответе.

Спасибо @Remy Lebeau за то, что он указал, что это все еще проблема в С++ 17 (хотя и не так): конструктор shared_ptr может не выделить свой управляющий блок и выполнить throw, и в этом случае переданный ему указатель не удаляется.

Решение: используйте make_shared.

  • 5
    Другое решение: никогда не выделяйте динамически более одного объекта в строке.
  • 2
    @Antimony: Да, гораздо более заманчиво выделить более одного объекта, когда вы уже выделили один, по сравнению с тем, когда вы еще не выделяли.
Показать ещё 5 комментариев
14

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

Распределение объектов в стеке, опираясь на их неявное разрушение, является простой моделью. Если требуемая область действия объекта подходит для этой модели, тогда нет необходимости использовать new(), с привязкой delete() и проверкой указателей NULL. В случае, когда у вас есть много недолговечных объектов, выделение в стеке должно уменьшить проблемы фрагментации кучи.

Однако, если время жизни вашего объекта должно превышать текущую область, то new() - правильный ответ. Просто убедитесь, что вы обращаете внимание на то, когда и как вы называете delete(), и возможности указателей NULL, используя удаленные объекты и все другие ошибки, которые идут с использованием указателей.

  • 8
    «если время жизни вашего объекта должно выходить за пределы текущей области видимости, тогда new () является правильным ответом» ... почему бы не вернуть преимущественно значение или не принять переменную в пределах вызывающей стороны с помощью const ref или указателя ...?
  • 2
    @ Тони: Да, да! Я рад слышать, что кто-то защищает ссылки. Они были созданы, чтобы предотвратить эту проблему.
Показать ещё 1 комментарий
13

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

Class var;

он помещается в стек.

Вам всегда нужно вызвать destroy на объект, который вы разместили в куче, с новым. Это открывает возможности утечки памяти. Объекты, помещенные в стек, не подвержены утечке памяти!

  • 2
    +1 «[куча] обычно используется, когда вы ожидаете расширения» - например, добавление к std::string или std::map , да, острое понимание. Моей первоначальной реакцией было «но также очень часто отделить время жизни объекта от области действия создаваемого кода», но на самом деле лучше возвращать по значению или принимать значения в области вызывающего абонента по const ссылке или указателю, за исключением случаев, когда есть «расширение» участвует тоже. Хотя есть и другие способы использования звука, такие как заводские методы ....
11

Одна из важных причин избежать чрезмерного использования кучи для производительности - это, в частности, использование механизма управления памятью по умолчанию, используемого С++. Хотя распределение может быть довольно быстрым в тривиальном случае, выполнение большого количества new и delete на объектах неравномерного размера без строгого порядка приводит не только к фрагментации памяти, но также усложняет алгоритм распределения и может полностью уничтожить производительность в определенных случаях.

Проблема с тем, что пулы памяти, которые созданы для решения, позволяя смягчить присущие ему недостатки традиционных реализаций кучи, в то же время позволяя вам используйте кучу по мере необходимости.

Лучше все же, чтобы избежать проблемы вообще. Если вы можете поместить его в стек, сделайте это.

  • 0
    Вы всегда можете выделить достаточно большой объем памяти, а затем использовать размещение new / delete, если скорость является проблемой.
  • 0
    Пулы памяти призваны избежать фрагментации, ускорить освобождение (одно освобождение для тысяч объектов) и сделать его более безопасным.
10

Я склонен не соглашаться с идеей использования нового "слишком много". Хотя использование оригинального плаката нового с системными классами немного смешно. (int *i; i = new int[9999];? действительно? int i[9999]; намного яснее.) Я думаю, что это то, что получало кокер-комментатор.

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

Однако, когда вы работаете со своими собственными классами/объектами (например, исходный класс Line poster), вы должны начать думать о проблемах, таких как объем памяти, постоянство данных и т.д. самостоятельно. На данный момент использование нескольких ссылок на одно и то же значение неоценимо - оно позволяет создавать такие конструкции, как связанные списки, словари и графики, где несколько переменных должны иметь не только одно и то же значение, но и ссылаться на один и тот же объект в памяти. Однако класс Line не имеет ни одного из этих требований. Таким образом, исходный код плаката на самом деле абсолютно не нужен для new.

  • 0
    обычно new / delete будет использоваться, если вы не знаете заранее размер массива. Конечно, std :: vector скрывает новый / удалить для вас. Вы все еще используете их, но через std :: vector. Так что в настоящее время он будет использоваться, когда вы не знаете размер массива и по какой-то причине хотите избежать издержек std :: vector (который небольшой, но все еще существует).
  • 0
    When you're working with your own classes/objects ... у вас часто нет причин делать это! Крошечная доля Qs находится на деталях конструкции контейнера опытными программистами. В противоположности этому , угнетающая доля около смешения новичков , которые не знают , что STDLIB существует - или активно заданных жуткие заданий в «программировании» «курсы», где репетитор требование , которые они бесцельно изобретать колесо - прежде , чем они даже узнал что такое колесо и почему оно работает. Содействуя более абстрактному распределению, C ++ может спасти нас от бесконечного «segfault со связанным списком» C; пожалуйста, давай позволим .
Показать ещё 1 комментарий
10

Я думаю, что плакат означал You do not have to allocate everything on the heap, а не stack.

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

3

Две причины:

  • В этом случае это не нужно. Вы делаете свой код ненужным более сложным.
  • Он выделяет пространство в куче, и это означает, что вы должны помнить delete позже, иначе это приведет к утечке памяти.
1

Еще одно замечание ко всем приведенным выше правильным ответам, это зависит от того, какое программирование вы делаете. Например, разработка ядра в Windows → Стек сильно ограничен, и вы не сможете воспринимать ошибки страницы, как в режиме пользователя.

В таких средах новые или C-подобные вызовы API предпочтительны и даже необходимы.

Конечно, это всего лишь исключение из правил.

1

new есть новый goto.

Напомним, почему goto настолько оскорблен: хотя он является мощным инструментом низкого уровня для управления потоком, люди часто использовали его излишне сложными способами, из-за которых сложный код не выполнялся. Более того, наиболее полезные и простые для чтения шаблоны были закодированы в структурированных операциях программирования (например, for или в while); конечный эффект заключается в том, что код, где goto является подходящим способом, довольно редок, если у вас возникает соблазн написать goto, вы, вероятно, делаете что-то плохо (если вы действительно не знаете, что делаете).

new аналогичен - он часто используется, чтобы сделать вещи излишне сложными и трудными для чтения, а наиболее полезные шаблоны использования могут быть закодированы, были закодированы в различные классы. Кроме того, если вам нужно использовать любые новые шаблоны использования, для которых еще нет стандартных классов, вы можете написать свои собственные классы, которые их кодируют!

Я бы даже утверждал, что new хуже, чем goto, из-за необходимости соединять new и delete утверждения.

Как и goto, если вы когда-нибудь думаете, что вам нужно использовать new, вы, вероятно, делаете что-то плохое, особенно если вы делаете это за пределами реализации класса, целью которого в жизни является инкапсуляция любых динамических распределений, которые вам нужно сделать.

1

Основная причина в том, что объекты в куче всегда сложны в использовании и управлении, чем простые значения. Написание кода, который легко читать и поддерживать, всегда является первоочередной задачей любого серьезного программиста.

Другой сценарий - библиотека, которую мы используем, предоставляет семантику значений и ненужное динамическое распределение. Std::string - хороший пример.

Однако для объектно-ориентированного кода использование указателя, которое означает использование new для его создания заранее, является обязательным. Чтобы упростить сложность управления ресурсами, у нас есть десятки инструментов, позволяющих сделать это как можно проще, например интеллектуальные указатели. Парадигма, основанная на объекте или генерическая парадигма, предполагает семантику значений и требует меньше или вообще не new, как отмечали другие плакаты.

Традиционные шаблоны проектирования, особенно те, о которых говорится в GoF, используйте new много, так как они являются типичным кодом OO.

  • 3
    Это ужасный ответ. For object oriented code, using a pointer [...] is a must : нонсенс . Если вы обесцениваете «OO», ссылаясь только на небольшое подмножество, полиморфизм - тоже нонсенс: ссылки тоже работают. [pointer] means use new to create it beforehand : особенно бессмыслица : ссылки или указатели могут быть взяты на автоматически размещенные объекты и использованы полиморфно; следи за мной [typical OO code] use new a lot : может быть, в какой-то старой книге, но кого это волнует? Любой смутно современный C ++ избегает new / необработанных указателей везде, где это возможно - и ни в коем случае не является чем-то вроде OO, делая это
-4

new выделяет объекты в куче. В противном случае объекты выделяются в стеке. Посмотрите разницу между двумя.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню