Неправильно ли использовать удаление вне области видимости, как это?

int* func()
{
   int* i=new int[3];
   return i; 
}

void funcc()
{
   int* tmp=func();
   //delete allocated memory after use
   delete[] tmp;
}

Я считаю, что компилятор при компиляции funcc не может знать, что нужно удалить 3 int, например, если func находится в другом файле.


person user3514491    schedule 23.04.2014    source источник
comment
Я бы подумал, что delete чаще всего будет использоваться вне области видимости в том смысле, что вы не отпускаете указатель в той же области, в которой вы его выделяете. Право собственности на указатель определяется разработчиком приложения. Точка, в которой он должен быть выпущен, может не совпадать с точкой, в которой он был выделен.   -  person Brandon Buck    schedule 23.04.2014


Ответы (2)


Это описано в Часто задаваемые вопросы по C++ lite:

[16.14] После того, как p = new Fred[n], как компилятор узнает, что во время delete[] p нужно уничтожить n объектов?

Краткий ответ: Магия.

Длинный ответ: система времени выполнения хранит количество объектов n где-то, откуда его можно получить, если вы знаете только указатель, p. Есть два популярных метода, которые делают это. Оба эти метода используются компиляторами коммерческого уровня, оба имеют компромиссы, и ни один из них не идеален. Эти методы:

  • Перераспределите массив и поместите n слева от первого объекта Fred.
  • Используйте ассоциативный массив с p в качестве ключа и n в качестве значения.
person Philipp    schedule 23.04.2014

Именно так предполагается использовать delete, и это нормально. Размер массива хранится где-то за кулисами, так что delete[] будет поступать правильно, где бы это ни происходило.

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

#include <vector>

std::vector<int> func() {
    return std::vector<int>(3);
}

void funcc() {
    auto tmp = func();
    // no need to do anything - the vector frees its memory automatically
}
person Mike Seymour    schedule 23.04.2014
comment
Или рассмотрите возможность использования boost::scoped_array или boost::shared_array, которые управляют временем жизни массива для вас. - person Dale Wilson; 23.04.2014
comment
@DaleWilson: scoped_array, будучи привязанным к области, не может быть возвращено из функции. shared_array (или его стандартный эквивалент) полезен, если вы хотите совместное владение, но слишком сложен, если вы этого не сделаете. - person Mike Seymour; 23.04.2014
comment
auto здесь — неплохое запутывание. - person James Kanze; 23.04.2014
comment
@JamesKanze: C++11 уже 2 года, и стиль вывода типов auto является общим для многих других языков. На данный момент это не совсем запутанность. - person Zan Lynx; 23.04.2014
comment
@ZanLynx C++ (11 или нет) — язык со статической типизацией; это не питон. Он предназначен для использования в больших проектах (в отличие от Python), где многие люди должны прочитать и понять код позже. Есть несколько особых случаев, когда auto допустимо; где инициализатор делает тип настолько очевидным, что не имеет значения, что он не прописан, но это явно не один из них. - person James Kanze; 23.04.2014
comment
@JamesKanze: разница во мнениях. Мой код C++ до C++11 будет иметь здесь typedef (func_type_t?), который может быть не намного более информативным, чем auto. Но с кодом намного проще работать, когда я решаю переключаться между вектором, массивом или двухсторонней очередью. - person Zan Lynx; 23.04.2014
comment
@JamesKanze: И я бы хотел, чтобы вы печатали std::vector< std::map<std::string, std::pair<std::string, int> > > каждый раз, когда вам это нужно, вместо того, чтобы использовать typedef или auto. - person Zan Lynx; 23.04.2014
comment
@JamesKanze: Для меня это короче и читабельнее, чем избыточное имя типа - полная противоположность запутыванию. Я предполагаю, что это может сбить с толку тех, кто отказывается идти в ногу с языком, но я не могу усложнить свои ответы, придерживаясь исторических идиом для их же пользы. - person Mike Seymour; 24.04.2014
comment
@ZanLynx Вы имеете в виду, что его намного легче сломать. Такие вещи, как достоверность итератора, меняются при изменении типа. Есть причина, по которой языки с утиной типизацией не используются в больших проектах. - person James Kanze; 24.04.2014
comment
@MikeSeymour Это не имеет ничего общего с историей языка. auto полезен в определенных случаях. Но его систематическое использование — это анти-шаблон, который усложняет сопровождение программы. Тот, кто модифицирует код, должен знать точный тип, чтобы модифицировать его безопасно. Изменение типа может привести к поломке кода и должно потребовать явного изменения этого кода (а не какого-то постороннего кода). - person James Kanze; 24.04.2014
comment
@JamesKanze: делать вывод о том, что я предлагаю вам использовать его систематически из одного примера, почти так же странно, как сравнивать неявную статическую типизацию с динамической типизацией Python. На мой взгляд, разумно использовать его всякий раз, когда вам не нужен точный тип (который можно легко найти, если вы хотите его узнать); если вас это волнует, то, на мой взгляд, это запах: код может быть слишком тесно связан с тем, что создает объект. Явное указание типа все еще может вызвать проблемы (например, неожиданные неявные преобразования), если он изменится, поэтому код не становится менее уязвимым. - person Mike Seymour; 24.04.2014