'New' и 'delete' устарели в C ++? - PullRequest
       5

'New' и 'delete' устарели в C ++?

68 голосов
/ 20 января 2020

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

while(T--) {
   int N;
   cin >> N;
   int *array = new int[N];
   // Do something with 'array'
   delete[] array;
}

Однако я увидел, что одно из решений позволило следующий случай:

while(T--) {
    int N;
    cin >> N;
    int array[N];
    // Do something with 'array'
}

После небольшого исследования я прочитал, что g ++ позволяет это, но это заставило меня задуматься, в каких случаях тогда необходимо использовать динамическое распределение c? Или компилятор переводит это как динамическое выделение c?

Функция удаления включена. Однако обратите внимание, что здесь речь идет не об утечках памяти.

Ответы [ 7 ]

114 голосов
/ 20 января 2020

Ни один из отображаемых вами фрагментов не является идиоматическим c, современным кодом C ++.

new и deletenew[] и delete[]) не устарели в C ++ и никогда не будут устаревшими. Они по-прежнему способ создания экземпляров динамически распределенных объектов. Однако, поскольку вы всегда должны сопоставлять new с deletenew[] с delete[]), их лучше хранить в (библиотечных) классах, которые гарантируют это для вас. См. Почему программисты на C ++ должны минимизировать использование 'new'? .

Ваш первый фрагмент использует «голый» new[], а затем delete[] не создает созданный массив. Это проблема. std::vector делает все, что вам нужно, здесь просто отлично. Он будет использовать некоторую форму new за кулисами (я не буду вдаваться в детали реализации), но для всего, что вам нужно, это динамический массив c, но лучше и безопаснее.

Ваш Второй фрагмент использует «массивы переменной длины» (VLA), функцию C, которую некоторые компиляторы также допускают в C ++ в качестве расширения. В отличие от new, VLA в основном расположены в стеке (очень ограниченный ресурс). Но что более важно, они не являются стандартной функцией C ++, и их следует избегать, потому что они не переносимы. Они, конечно, не заменяют динамическое распределение c (т.е. кучу).

22 голосов
/ 20 января 2020

Ну, для начала, new / delete не устаревают.

В вашем конкретном случае c они, однако, не единственное решение. То, что вы выберете, зависит от того, что скрыто под вашим комментарием «сделать что-то с массивом».

Во втором примере используется нестандартное расширение VLA, которое пытается разместить массив в стеке. Это имеет определенные ограничения, а именно: ограниченный размер и невозможность использования этой памяти после того, как массив выходит из области видимости. Вы не можете переместить его, он «исчезнет» после того, как стек развернется.

Так что, если ваша единственная цель - выполнить локальные вычисления, а затем выбросить данные, это может на самом деле работать нормально. Однако более надежным подходом было бы динамическое выделение памяти, предпочтительно с std::vector. Таким образом, вы получаете возможность создавать пространство для ровно столько элементов, сколько вам нужно, основываясь на значении времени выполнения (к чему мы стремимся все время), но оно также хорошо очищается и вы можете его убрать этой области, если вы хотите сохранить память для дальнейшего использования.

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

15 голосов
/ 20 января 2020

Ваш второй пример использует массивы переменной длины (VLA), которые на самом деле являются функцией C99 ( not C ++!), Но тем не менее поддерживаются g ++ .

См. также этот ответ .

Обратите внимание, что массивы переменной длины отличаются от new / delete и не "устарели" в любом случае.

Имейте в виду, что VLA не ISO C ++.

13 голосов
/ 21 января 2020

Современный C ++ предоставляет более простые способы работы с динамическими c выделениями. Интеллектуальные указатели могут позаботиться об очистке после исключений (которые могут произойти где угодно, если это разрешено) и досрочного возврата, как только указанные структуры данных go выйдут из области видимости, поэтому может иметь смысл использовать их вместо:

  int size=100;

  // This construct requires the matching delete statement.
  auto buffer_old = new int[size];

  // These versions do not require `delete`:
  std::unique_ptr<int[]> buffer_new (new int[size]);
  std::shared_ptr<int[]> buffer_new (new int[size]); 
  std::vector<int> buffer_new (size);  int* raw_access = buffer_new.data();

Из C ++ 14 вы также можете написать

auto buffer_new = std::make_unique<int[]>(size);

, это выглядит еще лучше и предотвратит утечку памяти в случае сбоя выделения. Начиная с C ++ 20, вы сможете сделать столько, сколько

auto a = std::make_shared<int[]>(size);

, что для меня все еще не компилируется во время написания g cc 7.4.0. В этих двух примерах мы также используем auto вместо объявления типа слева. Во всех случаях используйте массив как обычно:

buffer_old[0] = buffer_new[0] = 17;

Утечки памяти из new и падения из-за удвоения delete - это то, что C ++ было разбито в течение многих лет, являясь "центральной точкой" аргументации для переключение на другие языки. Может быть, лучше избегать.

3 голосов
/ 26 января 2020

new и delete не устаревают.

Объекты, созданные новым оператором, могут передаваться по ссылке. Объекты могут быть удалены с помощью delete.

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

Оператор - int array [N] - это способ определения массива. Массив может использоваться в пределах объема кодового блока. Он не может быть передан подобно тому, как объект передается другой функции.

2 голосов
/ 20 января 2020

В первом примере требуется delete[] в конце, иначе у вас будет утечка памяти.

Во втором примере используется переменная длина массива, которая не поддерживается C ++; разрешает только константные выражения для длины массива .

. В этом случае полезно использовать std::vector<> в качестве решения; это оборачивает все действия, которые вы можете выполнить над массивом, в класс шаблона.

0 голосов
/ 23 января 2020

Синтаксис выглядит как C ++, но идиома похожа на обычный старый Algol60. Обычно такие блоки кода были такими:

read n;
begin
    integer array x[1:n];
    ... 
end;

Пример можно записать так:

while(T--) {
    int N;
    cin >> N;
    {
        int array[N];
        // Do something with 'array'
    }
}

Иногда я пропускаю это в современных языках;)

...