C++: является ли push_back(new Object()) утечкой памяти?

Является ли следующий код C++ утечкой памяти?

list.push_back(new String("hi"));

Насколько я понимаю, push_back из любой стандартной коллекции/контейнера всегда делает копию. Итак, если новая строка скопирована, ничто никогда не сможет удалить новую строку, верно? так как после push_back на него нет ссылки...

Я прав или неправ здесь?

Спасибо.

Джбу

edit: я думаю, что ошибаюсь, так как new вернет указатель... у нас всегда будет указатель, чтобы иметь возможность удалить новую строку


person jbu    schedule 24.11.2010    source источник
comment
Это зависит от определения list.   -  person Steve Townsend    schedule 24.11.2010
comment
Зависит от того, что такое list. Предполагая, что это std::list<String*>, @UncleBens прав: большую часть времени все еще можно правильно очистить. Но вы должны сделать эту очистку вручную; std::list не будет делать это за вас.   -  person aschepler    schedule 24.11.2010
comment
да. Потому что переменная list на самом деле имеет тип, у которого есть функция push_back с пустым телом.   -  person Edward Strange    schedule 24.11.2010


Ответы (7)


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

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

person UncleBens    schedule 24.11.2010
comment
если оператор выдает исключение, и вы не поймаете и не обработаете его должным образом - в случае этого оператора нет никакого способа правильно его обработать. Если push_back выбрасывает, значит, он не сохранил указатель, и его невозможно освободить, потому что этот вызывающий код также не имеет указателя. Я полагаю, теоретически вы могли бы что-то сделать с String::operator new, но это не звучит весело. - person Steve Jessop; 24.11.2010

Да, но не по той причине, о которой вы думаете.

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

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

person jalf    schedule 24.11.2010
comment
Извините, но я не мог понять, копируется ли указатель или данные, на которые он указывает, также копируются? Например: - String *str = new String(HI); список.push_back(ул); Теперь я могу вызвать удаление str и по-прежнему иметь доступ к HI из списка, потому что valgrind показывает утечку в list.push_back без каких-либо вызовов удаления. - person Vineet Deoraj; 11.07.2014

Если бы я увидел этот код, я бы очень заподозрил, что возможна утечка памяти. На первый взгляд кажется, что выделенный String* добавляется в list<String*>. По моему опыту, за этим часто следует плохой код обработки ошибок, который не освобождает должным образом выделенную память.

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

class Container {
  ~Container() {
    std::list<String*>::iterator it = list.begin();
    while (it != list.end()) {
      delete *it;
      it++;
    }
  }

  void SomeMethod() {
    ...
    list.push_back(new String("hi"));
  }

  std::list<String*> list;
}

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

ИЗМЕНИТЬ

Как указал aschepler, утечка все еще существует, если метод push_back выдает исключение.

person JaredPar    schedule 24.11.2010
comment
Если вызов push_back выдает исключение, это все равно утечка. - person aschepler; 24.11.2010
comment
Также существует риск двойного удаления, если вы не объявите конструктор копирования и оператор присваивания копии. - person James McNellis; 24.11.2010

Вы правы, если ничто не удаляет строку, когда она удаляется из списка.

person Matt K    schedule 24.11.2010

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

И лучший способ добиться этого — использовать интеллектуальный указатель. Например, shared_ptr Boost или shared_ptr C++0x.

person John Dibling    schedule 24.11.2010
comment
масштабируемый_ptr?? Вы имеете в виду shared_ptr? - person Fred Larson; 24.11.2010
comment
Единственное, что я мог подумать, вы имели в виду, это scoped_ptr, но это не будет работать в контейнере, поскольку его нельзя копировать. - person Fred Larson; 24.11.2010
comment
@Fred: Вероятно, это будущая версия Boost :) - person Default; 24.11.2010

list.push_back(new String("hi"));

Почему вы выделяете динамические строки в первую очередь? Если вы не хотите обмениваться данными между различными частями вашей программы, изменяя строки (что было бы довольно необычно), избавьтесь от указателя:

std::list<std::string> list;         // note: no pointer!
list.push_back(std::string("hi"));   // explicitly create temporary
list.push_back("hi");                // alternative: rely on coercion
person fredoverflow    schedule 24.11.2010

No.

Вы можете удалить объект, выполнив:

delete list[i];
list.erase(list.begin() + i);

или очистить весь список:

for (unsigned int i = 0; i < list.size(); ++i)
{
  delete list[i];
}
list.clear();
person Snowfish    schedule 24.11.2010