MyVector.erase (myPtr) удаляет объект, указанный myPtr? - PullRequest
7 голосов
/ 11 ноября 2008

Если у меня есть следующий код,

Foo *f = new Foo();
vector<Foo*> vect;
vect.push_back(f);
// do stuff
vect.erase(f);

Я создал утечку памяти? Наверное, но слово стереть создает ощущение, что оно его удаляет.

Когда я пишу это, мне интересно, не является ли ошибкой поместить указатель в вектор STL. Что ты думаешь?

Ответы [ 7 ]

8 голосов
/ 11 ноября 2008

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

Нет ничего необычного в том, чтобы поместить указатель в стандартный контейнер библиотеки. Проблема, однако, заключается в том, что вы должны следить за его удалением при удалении из контейнера. Лучший, но простой способ сделать это - использовать boost :: shared_ptr:

{ 
    boost::shared_ptr<foo> f(new foo);

    std::vector< boost::shared_ptr<foo> > v;
    v.push_back(f);
    v.erase(v.begin());
} /* if the last copy of foo goes out of scope, the memory is automatically freed */

Следующий стандарт C ++ (обычно называемый C ++ 1x и C ++ 0x) будет включать std::shared_ptr. Там вы также сможете использовать std::unique_ptr<T>, что быстрее, так как не позволяет копировать. Использование std::unique_ptr с контейнерами в c ++ 0x похоже на библиотеку ptr_container в boost.

4 голосов
/ 11 ноября 2008

Другой вариант - использовать Boost Pointer Containers . Они предназначены для того, чтобы делать именно то, что вы хотите.

2 голосов
/ 12 ноября 2008

Чтобы выяснить, почему указатель не удален, рассмотрим

std::vector<char const*> strings;
strings.push_back("hello");
strings.push_back("world");
// .erase should not call delete, pointers are to literals

std::vector<int*> arrays;
strings.push_back(new int[10]);
strings.push_back(new int[20]);
// .erase should call delete[] instead of delete

std::vector<unsigned char*> raw;
strings.push_back(malloc(1000));
strings.push_back(malloc(2000));
// .erase should call free() instead of delete

В общем, vector<T*>::erase не может угадать, как бы вы избавились от T*.

2 голосов
/ 11 ноября 2008

В качестве альтернативы есть boost :: ptr_vector container .

Он знает, что держит указатели, которыми владеет, и, таким образом, автоматически удаляет их.

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

Foo *f = new Foo();
boost::ptr_vector<Foo>  vect;
vect.push_back(f);
// do stuff
vect.erase(f);
1 голос
/ 11 ноября 2008

STL контейнеры не освободят вашу память. Лучший совет - использовать умные указатели, зная, что std :: auto_ptr не поместится внутри контейнеров. Я бы порекомендовал boost :: shared_ptr, или если у вашего поставщика компилятора есть поддержка расширений TR1 (многие это делают), вы можете использовать std :: tr1 :: shared_ptr.

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

1 голос
/ 11 ноября 2008

vector удаляет содержащиеся в нем данные. Поскольку ваш вектор содержит указатели, он удаляет только указатели, а не данные, на которые они могут или не могут указывать.

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

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

vector<Foo> vect;
vect.push_back(Foo());
// do stuff
vect.erase(f);

Конечно, это предполагает, что Foo является копируемым, и что его конструктор копирования не слишком дорогой, но он позволяет избежать утечек памяти, и вам не нужно забывать удалять объект Foo. Другой подход заключается в использовании интеллектуальных указателей (таких как Boost's shared_ptr), но вам может вообще не понадобиться семантика указателей, и в этом случае простое решение является лучшим.

1 голос
/ 11 ноября 2008

Это определенно не ошибка - указывать указатель на стандартный контейнер (однако, сделать контейнер auto_ptr ошибочно). Да, вам нужно явно удалить, чтобы освободить память, на которую указывают отдельные элементы, или вы можете использовать один из интеллектуальных указателей повышения .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...