Зачем удалять в векторе экземпляры подстановки? - PullRequest
1 голос
/ 29 февраля 2012

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

На каком-то форуме я только что нашел этот фрагмент кода:

std::vector<MyClass*> myvec;
for(unsigned int i = 0; i < 100; ++i) {
  myvec.push_back(new MyClass( foo1 ));
}

// somewhere in the code inside a particular if statement
MyClass* replacement = new MyClass( foo2 );
delete myvec[0];
myvec[0] = replacement;

У меня есть вектор экземпляров MyClass, и где-то в коде я должен заменить некоторые экземпляры другими.

Почему я должен вызвать удаление? Достаточно ли заменить указатель?

Чему я научился:

Ответы [ 7 ]

5 голосов
/ 29 февраля 2012

Если вы не delete оригинал MyClass, он останется рядом, но будет недоступен, так как вы стерли указатель на него.Это утечка памяти .

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

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

2 голосов
/ 29 февраля 2012

Одна из самых больших проблем в C ++ - какой тип хранить в векторе объектов.

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

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

  • Вы можете сохранить вектор shared_ptr, и это обычно делается.Он не идеален, поскольку право собственности на объект, по-видимому, находится в векторе.Делая это, вы не столкнетесь с трудностями, но это не самый совершенный способ работы с большой коллекцией объектов.

  • boost предоставляет специальный вектор для указателя, который управляет временем жизни для вас,Может быть хорошей альтернативой

  • Новый C ++ 11 позволит использовать векторы подвижных объектов, и я думаю, что вектор unique_ptr также может быть разрешен (вектор auto_ptr - нет).

Это довольно субъективно, но в идеале нужно ввести typedef к типу указателя и затем использовать вектор этого typedef.Позже вы можете изменить typedef, если он вам подходит.shared_ptr, вероятно, сделает эту работу за вас.

Теперь все заканчивается так:

typedef spns::shared_ptr< MyClass > MyClassPtr; 
      // spns is an alias to the namespace you use for shared_ptr, either std or boost
std::vector<MyClassPtr> myvec;
for(unsigned int i = 0; i < 100; ++i) 
{
  myvec.push_back(MyClassPtr( new MyClass( foo1 ));
  // but with C++11 if you still use shared_ptr replace with
 // myvec.push_back( spns::make_shared<MyClass>(foo1) );
}

// somewhere in the code inside a particular if statement
MyClass* replacement = new MyClass( foo2 );
myvec[0] = MyClassPtr( replacement );

 // preferred alternative to above 2 lines
 //  myvec[0].reset( new MyClass(foo2) );
2 голосов
/ 29 февраля 2012

Вам необходимо явно освободить память, иначе она останется выделенной, но недоступной.Это не Java или C #, где сборщик мусора позаботится об этом.

1 голос
/ 29 февраля 2012

при звонке

new MyClass( foo1 )

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

Таким образом, для каждого вызова new у вас должен быть соответствующий вызов для удаления где-то в коде.

1 голос
/ 29 февраля 2012

Стандартный контейнер указателей никогда не удалит цель из любых указателей, которые он содержит.Он не может знать, что они указывают на объекты, созданные с помощью new - они могли бы быть созданы с помощью new[] (в этом случае требуется delete[]), или они могут быть указателями на статические или автоматические объекты, которые должныне может быть удалено, или может быть что-то еще, ответственное за их удаление.

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

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

1 голос
/ 29 февраля 2012

std::vector<MyClass*> - это вектор указателей. Он не управляет временем жизни объектов, которые он содержит - он только управляет памятью с массивом указателей, которые он представляет.

Если вы неправильно управляете delete s, вы получите утечки (если только вы не удалите дважды из-за другой ошибки).

0 голосов
/ 29 февраля 2012

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

...