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
s существует для объекта, а когда последний перестает существовать (число отсчитывается до нуля), оно освобождает указатель. Копирование просто увеличивает счетчик ссылок (и перемещает право собственности на более низкую, почти бесплатную стоимость). Вы делаете их с 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<T>
имел очевидное применение в C++ 03, я не могу говорить об актуальности в настоящее время потому что мы можем использовать std::vector<std::unique_ptr<T>>
, возможно, с небольшими сопоставимыми накладными расходами, но это требование должно быть проверено.
Независимо от того, никогда не освобождает вещи в вашем коде. Оберните вещи, чтобы убедиться, что управление ресурсами обрабатывается автоматически. У вас не должно быть никаких указателей владения в вашем коде.
Как по умолчанию в игре, я бы, вероятно, пошел с std::vector<std::shared_ptr<T>>
. Мы ожидаем совместного использования, это достаточно быстро, пока профилирование не говорит иначе, это безопасно и легко использовать.
Вы используете C++? –
Может быть, родной английский говорящий может расшифровать то, что вы хотите сказать, но я потерян. Во-первых, вы говорите об утечках памяти -> зависит от языка/платформы; Я ожидаю, что вы имеете в виду C++. Облегчение утечек памяти уже обсуждалось (http://stackoverflow.com/search?q=c%2B%2B+raii). Для правильной работы вам нужен виртуальный деструктор для удаления из базового типа. – gimpf
Что вы подразумеваете под "векторами указателей"? Вы имеете в виду «векторы ** ** указателей»? –