Nano Hash - криптовалюты, майнинг, программирование

Как правильно освободить вектор С++?

Возможный дубликат:
Как правильно освободить или удалить вектор c++?

У меня возникли проблемы с удалением памяти, выделенной в векторе. Несмотря на то, что я вызываю list.clear(), это не освобождает память.

Итак, у меня есть такой код в классе на основе шаблона с именем Set

template <class T>
class Set {
public:
    // stuff
private:
    int size;
    std::vector<T> list;
};

А в конструкторе я выделил память под вектор. Итак, я вызываю list = new std::vector;

Для вашего интереса, вот мой конструктор копирования и оператор присваивания, которые я также написал, где я также выделяю память для вектора:

template <class T>
Set<T>::Set(const Set& aSet)
{
    size = aSet.size;
    list->clear();
    list = new vector<T>;
    for (int i = 0; i < size; ++i) {
        list[i] = aSet.list[i];
    }
}

template <class T>
Set<T>& Set<T>::operator=(const Set& right)
{
    if (this != &right) {
        list->clear();
        size = right.size;
        list = new vector<T>;
        for (int i = 0; i < size; ++i) {
            list[i] = right.list[i];
        }
    }
    return (*this);
}

В деструкторе у меня просто есть list.clear(), чтобы удалить все элементы, а затем освободить память.

Но проблема в том, что когда я запускаю valgrind в моем файле .out, он говорит мне, что я определенно потерял часть памяти, и я не знаю, почему он мне это говорит. Я прочитал несколько вопросов здесь, в Stackoverflow, но в основном перепробовал все. Я попробовал очистить(), а затем удалить вектор, но это не сработало. Затем я попытался стереть (list.begin(), list.end()), но это тоже не сработало.

Мой мыслительный процесс заключается в том, что я использую Set *aSet = new Set; в моем основном классе, и поскольку int не является объектом, он не освобождается, когда я вызываю list.clear(). Это правильно? Как мне правильно стереть память?

Спасибо за любую помощь.

Edit1 = изменен список* на setList

Мои новые конструкторы и оператор присваивания:

template <class T>
Set<T>::Set(const Set& aSet)
{
    size = aSet.size;
    setList.clear();
    setList = aSet.setList;
}

template <class T>
Set<T>& Set<T>::operator=(const Set& right)
{
    if (this != &right) {
        setList.clear();
        size = right.size;
        setList = right.setList;
    }
    return (*this);
}

Valgrind по-прежнему сообщает, что у меня такое же количество потерянной памяти. В моем деструкторе у меня все еще есть list.clear()

Журнал Валгринд:

==11398== 
==11398== HEAP SUMMARY:
==11398==     in use at exit: 62,969 bytes in 352 blocks
==11398==   total heap usage: 540 allocs, 188 frees, 68,046 bytes allocated
==11398== 
==11398== LEAK SUMMARY:
==11398==    definitely lost: 8,624 bytes in 14 blocks
==11398==    indirectly lost: 1,168 bytes in 5 blocks
==11398==      possibly lost: 4,829 bytes in 56 blocks
==11398==    still reachable: 48,348 bytes in 277 blocks
==11398==         suppressed: 0 bytes in 0 blocks
==11398== Rerun with --leak-check=full to see details of leaked memory

  • Убейте весь указатель и new дело. 07.11.2012
  • почему бы не использовать вектор по значению? Кроме того, вы пытаетесь создать std::set самостоятельно? Вы знаете о деструкторах? 07.11.2012
  • Каждому new нужен соответствующий delete. clear() удаляет все элементы из вектора и не влияет на память самого векторного объекта. 07.11.2012
  • @DmitryLedentsov Я пытаюсь создать свой собственный класс Set по шаблону, так как это моя домашняя работа. 07.11.2012
  • @chris Поскольку это набор классов шаблонов, я хочу также иметь возможность добавлять объекты в набор, поэтому мне нужны новые и все указатели. 07.11.2012
  • @watabou: Нет, не знаешь. Вам просто нужен вектор. 07.11.2012
  • @watabou, показанный вами код использует вектор только как одномерный массив, который вообще не требует использования указателей. Вы можете изменить размер, используя такие вещи, как push_back или resize. 07.11.2012
  • @watabou: Нет. Вы уже используете std::vector<>, поэтому вы должны понимать, что он реализует массив с динамическим размером, который, похоже, реализует ваш класс. Другими словами, вы можете просто использовать вектор векторов. Я использую набор *aSet = новый набор; в моем основном классе. Почему? Просто создайте его по значению. 07.11.2012
  • кстати, в шапке надо определить шаблоны 07.11.2012
  • @DmitryLedentsov да, я этим и занимаюсь. У меня есть только файлы Set.h и main.cpp. У меня есть объявление и определение в моем файле Set.h. 07.11.2012
  • @GManNickG Хорошо, не могли бы вы пояснить, что вы подразумеваете под созданием по значению? Я просто делаю Set *aSet; вот и все? Если я попытаюсь добавить элемент после этого, я получу ошибку сегментации, поскольку мне сообщают, что мой набор не был инициализирован. 07.11.2012
  • @watabou: Не делайте этого: std::vector<T> *list; сделайте это std::vector<T> list; (обратите внимание на отсутствующую звездочку). Затем он создает вектор как значение, и все управление памятью обрабатывается правильно. 07.11.2012
  • @LokiAstari Хорошо, я сделаю это сейчас. Я думаю, что у меня были некоторые другие ошибки, связанные с clang, в которых говорилось, что он не может найти жизнеспособный перегруженный оператор '=', поэтому я вставил туда *, чтобы я думал, что это исправит все ошибки. Для дальнейшего использования, когда мне нужно будет использовать указатель на вектор? 07.11.2012
  • Вам нужно использовать указатель на вектор, когда вам нужно динамически создавать и удалять векторы, вместо того, чтобы иметь один на все время жизни вашего объекта. Хотя даже тогда вам может понадобиться, скажем, вектор векторов. Вы также можете использовать указатель на вектор для случая, когда у вас может быть или не быть вектора, но опять же, для этих случаев часто есть лучшие решения, такие как boost::optional. 08.11.2012
  • Что такое setList? Откуда это вдруг? Что случилось с list? Это не может быть одно и то же переименование, потому что std::vector не имеет функции-члена addItem. Покажите весь ваш класс, как он есть сейчас, включая "// stuff" и особенно ваш деструктор. 08.11.2012
  • @BenjaminLindley А нет, здесь один человек сказал мне, что я не должен использовать список, поэтому я изменил каждый список на setList. Извините за путаницу. addItem — это мой метод добавления элементов в набор. 08.11.2012
  • Какой тип setList? 08.11.2012
  • @BenjaminLindley Ой, я понимаю, что вы говорите. Я изменил addItem на push_back. Прости, это было глупо. Я слишком много внимания уделял валгринду. Спасибо. setList - это вектор‹T› 08.11.2012
  • Цикл for не нужен, вы можете просто назначить вектор сразу, т.е. setList = aSet.setList; -- Лучше было бы использовать список инициализаторов конструктора, но по одному. 08.11.2012
  • Попался, спасибо. Я отредактировал свой код сейчас. 08.11.2012

Ответы:


1

Мой мыслительный процесс заключается в том, что я использую Set *aSet = new Set; в своем основном классе, и, поскольку int не является объектом, он не освобождается, когда я вызываю list.clear(). Это правильно? Как мне правильно стереть память?

Нет. Чтобы удалить память, которую вы выделили правильно, вам нужно вызвать delete:

Set *aSet = new Set;

delete aSet;

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

Set aSet;
// no delete required. Variable destroyed/deallocated when it goes out of scope.

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

std::unique_ptr<Set> aSet(new aSet);

Интеллектуальные указатели реализуют RAII для динамического распределения, поэтому вам не нужно делать это вручную.

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


std::vector<T>::clear() не требуется для освобождения памяти вектора. Вы можете использовать функцию-член С++ 11 shrink_to_fit() или использовать трюк подкачки:

std::vector<int> list;

...

std::vector<int>(list).swap(list);

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

07.11.2012
  • Интересно. Спасибо, я этого не знал. Я думаю, что причина, по которой я создавал указатель на вектор, заключается в том, что я получил ошибки, говорящие о том, что он не может найти перегруженный '=' для list = new vector‹T›. Что я должен сделать, чтобы исправить это тогда? Люди говорят мне, что мне не нужно новое здесь. 07.11.2012
  • Вам не нужно new. Вы просто объявляете list вектором, std::vector<T> list; и затем не инициализируете его ничем в конструкторе; Он автоматически инициализируется с помощью конструктора вектора по умолчанию. 07.11.2012
  • Попался, спасибо. Это избавило от всех ошибок. Я отчитаюсь после того, как изменю эти вещи, а затем проверю valgrind на наличие потерянной памяти. 07.11.2012
  • Хорошо, я удалил все указатели на Set и вектор. Сейчас у меня просто Set‹int› aSet; в моем основном классе, и это работает, но valgrind раздражающе говорит, что я потерял память. Сейчас я не использую new нигде в своем коде. 08.11.2012
  • IIRC valgrind должен точно сообщать, где происходят потерянные распределения, если вы посмотрите на подробную информацию журнала. 08.11.2012
  • Если я запускаю valgrind с параметром --leak-check=full, он возвращает кучу бессмысленного (для меня) списка. Такие вещи, как: по 0x499F39: NXMapKeyCopyingInsert (в /usr/lib/libobjc.A.dylib) == 50605== по 0x498D9D: _read_images (в /usr/lib/libobjc.A.dylib) 08.11.2012
  • Вполне возможно, что утечка на самом деле не в вашем коде, а в какой-то используемой вами библиотеке. 08.11.2012
  • Итак, как правильно удалить вектор? Поскольку у меня нигде нет указателя на него, и я нигде в своем коде не вызываю new, я не понимаю, почему valgrind сообщает о той же точной потерянной памяти до того, как я удалил все указатели и new. В моем деструкторе есть vector‹T›(list).swap(list); но это все еще не работает. clear() тоже не работает. 08.11.2012
  • См. выше; тот факт, что valgrind сообщает об утечке, не означает, что вы делаете что-то неправильно. 08.11.2012
  • @ barnes53 Ну, я просто беспокоюсь, потому что задание говорит мне, что мне нужно управлять памятью, и я боюсь, что за любую потерянную память я получу баллы. Это действительно нормально, что valgrind сообщает о потере памяти? 08.11.2012
  • Это, вероятно, не «хорошо», но если ошибка не в вашем коде, вы не можете ее исправить. 08.11.2012
  • @ barnes53 Ладно, я думаю. Спасибо за помощь! 08.11.2012

  • 2

    Прежде чем вы сделаете new list, вам нужно сделать delete list, иначе вы получите утечку памяти, как вы обнаружили. Также нет необходимости clear список перед его удалением, деструктор автоматически очистит его. Изменить. Вам также необходимо удалить указатель в деструкторе класса Set.

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

    Более актуальный совет — использовать std::vector в качестве прямой переменной-члена вместо указателя. В этом случае вам обязательно нужно использовать clear.

    07.11.2012
  • Зачем ему в таком случае использовать clear? Когда Set будет уничтожен, его члены будут уничтожены, а ~vector будет вызван и автоматически очистит его, как если бы вы вызвали delete на vector*. 07.11.2012
  • Я не очень понимаю это. Если я удалю свой вектор перед использованием нового, не создаст ли это ошибку? Я просто попытался поместить список удаления, а затем список = новый вектор‹T›; в моем конструкторе, но, как я и подозревал, clang сказал, что освобождаемый указатель не был выделен. 07.11.2012
  • @watabou, вам нужно убедиться, что указатель установлен на NULL в конструкторе вашего класса. 07.11.2012
  • @abarnert, я предположил, что причина clear заключалась в том, что старое содержимое заменялось чем-то новым. Я недостаточно внимательно прочитал код, чтобы проверить это предположение. Если единственный раз, когда он очищается, это когда он уничтожен, тогда он действительно был бы излишним. 07.11.2012
  • @watabou Я думаю, он говорит, что в операции назначения вы должны удалить старый вектор перед созданием нового вектора. (По крайней мере, я на это надеюсь.) Очевидно, что в конструкторах вам нечего удалять перед вызовом new, а в деструкторе вы не хотите вызывать new после удаления, поэтому оператор присваивания копирования — единственное место, где это имеет смысл. 08.11.2012

  • 3

    Если вы работаете на C++11, вы можете использовать shrink_to_fit() . Насколько я понимаю, это не имеет обязательной силы, и реализация может помешать его фактическому сокращению.

    07.11.2012
  • Причина, по которой shrink_to_fit() не является обязательной, заключается в том, чтобы обеспечить умные реализации. Достойная реализация будет использовать это только тогда, когда есть реальные преимущества. Например, оптимизация малых строк позволяет в некоторых случаях полностью избежать динамического распределения, но была бы запрещена, если бы shrink_to_fit() было обязательным, поскольку оптимизация малых строк предполагает минимальную емкость. 07.11.2012
  • @bames53: std::vector<> не разрешено использовать оптимизацию с небольшим буфером. 08.11.2012
  • @GManNickG Это правильно, но вектор - не единственный контейнер с shrink_to_fit(). 08.11.2012
  • @bames53 bames53 в случае вектора, вызов всегда будет уменьшать емкость до размера? 08.11.2012
  • @NtscCobalt Это не гарантируется и в случае вектора. 08.11.2012
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..