Как избежать утечек памяти при использовании вектора указателей на динамично выделенные объекты в C++?

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

, различия могут быть:

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

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

Как примечание стороны, Этот поток QA имеет информацию о том, как выбрать зеркало для увеличения скорости загрузки.

66
задан Ciro Santilli 新疆改造中心法轮功六四事件 4 December 2016 в 14:33
поделиться

3 ответа

std :: vector будет управлять памятью за вас, как всегда, но эта память будет состоять из указателей, а не объектов.

Это означает, что ваши классы будут потеряны в памяти, как только ваш вектор будет перемещен вне рамок. Например:

#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<base*> container;

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(new derived());

} // leaks here! frees the pointers, doesn't delete them (nor should it)

int main()
{
    foo();
}

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

#include <algorithm>
#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<base*> container;

template <typename T>
void delete_pointed_to(T* const ptr)
{
    delete ptr;
}

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(new derived());

    // free memory
    std::for_each(c.begin(), c.end(), delete_pointed_to<base>);
}

int main()
{
    foo();
}

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

Было бы лучше, если бы указатели удаляли сами себя. Тезисы называются умными указателями, а стандартная библиотека предоставляет std :: unique_ptr и std :: shared_ptr .

std :: unique_ptr представляет собой уникальный (без общего доступа, с одним владельцем) указатель на некоторые ресурс. Это должен быть ваш умный указатель по умолчанию и полная полная замена любого использования необработанного указателя.

auto myresource = /*std::*/make_unique<derived>(); // won't leak, frees itself

std :: make_unique отсутствует в стандарте C ++ 11 по надзору, но вы можете создать его самостоятельно. Чтобы напрямую создать unique_ptr (не рекомендуется вместо make_unique , если вы можете), сделайте следующее:

std::unique_ptr<derived> myresource(new derived());

Уникальные указатели имеют только семантику перемещения; они не могут быть скопированы:

auto x = myresource; // error, cannot copy
auto y = std::move(myresource); // okay, now myresource is empty

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

#include <memory>
#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<std::unique_ptr<base>> container;

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(make_unique<derived>());

} // all automatically freed here

int main()
{
    foo();
}

shared_ptr имеет семантику копирования с подсчетом ссылок; это позволяет нескольким владельцам совместно использовать объект. Он отслеживает, сколько shared_ptr существует для объекта, и когда последний перестает существовать (этот счет становится равным нулю), он освобождает указатель. Копирование просто увеличивает количество ссылок (а перемещение передает право собственности по более низкой, почти бесплатной цене). Вы создаете их с помощью std :: make_shared (или напрямую, как показано выше, но поскольку shared_ptr должен выполнять внутреннее выделение памяти, его использование обычно более эффективно и технически более безопасно для исключений ] make_shared ).

#include <memory>
#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<std::shared_ptr<base>> container;

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(std::make_shared<derived>());

} // all automatically freed here

int main()
{
    foo();
}

Помните, обычно вы хотите использовать std :: unique_ptr по умолчанию, потому что он более легкий. Кроме того, std :: shared_ptr может быть построен из std :: unique_ptr (но не наоборот), так что можно начать с малого.

В качестве альтернативы вы можете использовать контейнер, созданный для хранения указателей на объекты, таких как boost :: ptr_container :

#include <boost/ptr_container/ptr_vector.hpp>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

// hold pointers, specially
typedef boost::ptr_vector<base> container;

void foo()
{
    container c;

    for (int i = 0; i < 100; ++i)
        c.push_back(new Derived());

} // all automatically freed here

int main()
{
    foo();
}

Хотя boost :: ptr_vector явно использовался в C ++ 03, я не могу говорить об актуальности сейчас, потому что мы можем использовать std :: vector > с небольшими или отсутствующими сопоставимыми накладными расходами, но это утверждение следует проверить.

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

По умолчанию в игре я, вероятно, выбрал бы std :: vector > . Мы в любом случае ожидаем совместного использования, это достаточно быстро, пока профилирование не говорит об обратном, это безопасно и просто в использовании.

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

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

По умолчанию в игре я, вероятно, выбрал бы std :: vector > . Мы в любом случае ожидаем совместного использования, это достаточно быстро, пока профилирование не говорит об обратном, это безопасно и просто в использовании.

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

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

По умолчанию в игре я, вероятно, выбрал бы std :: vector > . Мы в любом случае ожидаем совместного использования, это достаточно быстро, пока профилирование не говорит об обратном, это безопасно и просто в использовании.

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

По умолчанию в игре я, вероятно, выбрал бы std :: vector > . Мы в любом случае ожидаем совместного использования, это достаточно быстро, пока профилирование не говорит об обратном, это безопасно и просто в использовании.

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

По умолчанию в игре я, вероятно, выбрал бы std :: vector > . Мы в любом случае ожидаем совместного использования, это достаточно быстро, пока профилирование не говорит об обратном, это безопасно и просто в использовании.

145
ответ дан 24 November 2019 в 14:53
поделиться

Я предполагаю следующее:

  1. У вас есть такой вектор, как vector
  2. Вы нажимаете указатели на этот вектор после размещения объектов в куче
  3. Вы хочу выполнить push_back производного * указателя в этот вектор.

На ум приходят следующие вещи:

  1. Вектор не освобождает память объекта, на который указывает указатель. Вы должны удалить его сам.
  2. Ничего особенного для vector, но деструктор базового класса должен быть виртуальным.
  3. vector и vector <производный *> - это два совершенно разных типа.
9
ответ дан 24 November 2019 в 14:53
поделиться

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

Что вам нужно сделать, так это использовать интеллектуальный указатель:

vector< std::tr1::shared_ptr<Enemy> > Enemies;

(Если ваша std lib поставляется без TR1, используйте вместо него boost :: shared_ptr .) За исключением очень редких угловых случаев (циклических ссылок), это просто устраняет проблему времени жизни объекта.

Правка : Обратите внимание, что GMan в своем подробном ответе тоже упоминает об этом.

9
ответ дан 24 November 2019 в 14:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: