Как контейнеры stl удаляются? - PullRequest
6 голосов
/ 20 декабря 2008

Как уничтожаются объекты-контейнеры, такие как vector, в stl, даже если они создаются в куче?

EDIT

Если контейнер содержит указатели, то как уничтожить эти объекты-указатели

Ответы [ 12 ]

19 голосов
/ 20 декабря 2008

Контейнер-указатель STL НЕ очищает указанные данные. Это только очистит пространство, в котором находится указатель. Если вы хотите, чтобы вектор очищал данные указателя, вам нужно использовать интеллектуальную реализацию указателя:

{
    std::vector<SomeClass*> v1;
    v1.push_back(new SomeClass());

    std::vector<boost::shared_ptr<SomeClass> > v2;
    boost::shared_ptr<SomeClass> obj(new SomeClass);
    v2.push_back(obj);
}

Когда эта область заканчивается, оба вектора освободят свои внутренние массивы. v1 будет пропускать созданный SomeClass, поскольку в массиве находится только указатель на него. v2 не пропустит никаких данных.

5 голосов
/ 20 декабря 2008

Если у вас есть vector<T*>, ваш код должен удалить эти указатели перед удалением вектора: в противном случае эта память просочилась.

Знайте, что C ++ не выполняет сборку мусора, вот пример того, почему (извинения за синтаксические ошибки, уже давно я написал C ++):

typedef vector<T*> vt;
⋮
vt *vt1 = new vt, *vt2 = new vt;
T* t = new T;
vt1.push_back(t);
vt2.push_back(t);
⋮
delete vt1;

Последняя строка (delete vt1;) явно не должна удалять указатель, который она содержит; в конце концов, это также в vt2. Так что нет. И также не будет удаление vt2.

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

2 голосов
/ 20 декабря 2008

Используйте либо умные указатели внутри вектора, либо используйте ptr_vector boost. Это автоматически освободит выделенные объекты внутри него. Есть также карты, наборы и т. Д.

http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_vector.html и основной сайт: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_container.html

2 голосов
/ 20 декабря 2008

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

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

Самый простой способ удалить указатели - сделать:

for (ContainerType::iterator it(container.begin()); it != container.end(); ++it)
{
    delete (*it);
}
2 голосов
/ 20 декабря 2008

Это немного неправильно. Вектор, как и в большинстве контейнеров STL, состоит из 2 логических частей.

  1. экземпляр вектора
  2. фактическая реализация базового массива

Хотя настраиваемый, # 2 почти всегда живет в куче. Однако # 1 может жить либо в стеке, либо в куче, это зависит только от того, как оно распределено Например

void foo() { 
  vector<int> v;
  v.push_back(42);
}

В этом случае часть # 1 живет в стеке.

Теперь, как разрушается # 2? Когда первая часть вектора уничтожена, она уничтожит и вторую часть. Это делается путем удаления базового массива внутри деструктора векторного класса.

2 голосов
/ 20 декабря 2008

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

0 голосов
/ 05 августа 2009

Контейнеры STL похожи на любые другие объекты, если вы создаете экземпляр, он создается в стеке:

std::vector<int> vec(10);

Как и любая другая переменная стека, она живет только в той области функции, в которой она определена, и не нуждается в удалении вручную. Деструктор контейнеров STL вызовет деструктор всех элементов в контейнере.

Хранение указателей в контейнере - сложная задача. Поскольку у указателей нет деструкторов, я бы сказал, что вы никогда не захотите помещать необработанные указатели в контейнер STL. Сделать это безопасным способом исключения будет очень сложно, вам придется засорять ваш код блоками try {} finally {}, чтобы гарантировать, что содержащиеся в нем указатели всегда освобождаются.

Так что вы должны положить в контейнеры вместо сырых указателей? +1 jmucchiello за то, что поднял boost :: shared_ptr. boost :: shared_ptr безопасен для использования в контейнерах STL (в отличие от std :: auto_ptr). Он использует простой механизм подсчета ссылок и безопасен для использования в структурах данных, которые не содержат циклов.

Что вам нужно для структур данных, содержащих циклы? В этом случае вы, вероятно, захотите перейти на сборку мусора, что по сути означает использование другого языка, такого как Java. Но это другое обсуждение. ;)

0 голосов
/ 21 декабря 2008

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

Указатели обрабатываются одинаково.
Дело в том, что указатели являются данными POD. Конструктор копирования для указателя просто копирует адрес, а данные POD не имеют деструктора. Если вы хотите, чтобы контейнер управлял указателем, вам необходимо:

  • Используйте контейнер умных указателей. (например, общий указатель).
  • Используйте контейнер с усиленным ptr.

Я предпочитаю указатель контейнера:
Контейнеры указателей такие же, как контейнеры STL, за исключением того, что вы помещаете в них указатели, но контейнер затем становится владельцем объекта, на который указывает указатель, и, таким образом, освобождает объект (обычно вызывая delete), когда контейнер уничтожается.

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

int main()
{
    boost::ptr_vector<int>    data;

    data.push_back(new int(5));
    data.push_back(new int(6));

    std::cout << data[0] << "\n";  // Prints 5.
    std::cout << data[1] << "\n";  // Prints 6.


}   // data deallocated.
    // This will also de-allocate all pointers that it contains.
    // by calling delete on the pointers. Therefore this will not leak.

Следует также отметить, что интеллектуальные указатели в контейнере являются допустимой альтернативой, к сожалению, std :: auto_ptr <> не является правильным выбором интеллектуального указателя для этой ситуации.

Это связано с тем, что контейнеры STL предполагают, что содержащиеся в них объекты являются копируемыми, к сожалению, std :: auto_ptr <> не копируется в традиционном смысле, поскольку уничтожает исходное значение при копировании и, следовательно, источник копии не может быть Const.

0 голосов
0 голосов
/ 20 декабря 2008

Для удаления указанных элементов я написал простой функтор:

template<typename T>
struct Delete {
    void operator()( T* p ) const { delete p; }
};

std::vector< MyType > v;
// ....
std::for_each( v.begin(), v.end(), Delete<MyType>() );

Но вы должны использовать общие указатели, если содержимое вектора должно быть ... эм ... общим. Да.

...