C ++ std :: vector ошибок удаления и сегментации указателей - PullRequest
5 голосов
/ 21 мая 2009

У меня есть вектор указателей на класс. Мне нужно вызвать их деструкторов и освободить их память. Так как они являются векторными указателями, vector.clear () не выполняет эту работу. Поэтому я продолжил делать это вручную, вот так:

void Population::clearPool(std::vector<Chromosome*> a,int size)
{
    Chromosome* c;
    for(int j = 0 ;j < size-1;j++)
    {
       c = a.back();
       a.pop_back();
       delete c;
       printf("  %d \n\r",j);
       c = NULL;

    }

}

Печать там есть, потому что у меня есть говорящий деструктор, чтобы увидеть, в какой хромосоме происходит ошибка сегментации. Когда вызывается clearPool () и говорят, что мы получили размер 100, это может вызвать ошибку сегментации в любой хромосоме между 0 и 100.

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

Я использую IDE кодовых блоков и отладчик GDB. Трассировка стека при возникновении ошибки сегментации имеет 4 адреса памяти и функцию wsncpy().

Ответы [ 10 ]

21 голосов
/ 21 мая 2009
void Population::clearPool( std::vector <Chromosome*> & a )
{
   for ( int i = 0; i < a.size(); i++ ) {
      delete a[i];
   }
   a.clear();
}

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

Поскольку было опубликовано несколько шаблонных решений, использующих алгоритмы библиотеки C ++, вы также можете рассмотреть шаблонное решение, которое не:

template <class C> void FreeClear( C & cntr ) {
    for ( typename C::iterator it = cntr.begin(); 
              it != cntr.end(); ++it ) {
        delete * it;
    }
    cntr.clear();
}

С его помощью вы можете освободить любой контейнер динамически размещаемых объектов:

vector <Chromosome *> vc;
list <Chromosome *> lc;
// populate & use
FreeClear( lc );
FreeClear( vc );
5 голосов
/ 21 мая 2009

Небольшая модификация по сравнению с (@ 1800 ИНФОРМАЦИЯ).

  struct DeleteFromVector
    {
        template <class T>
        void operator() ( T* ptr) const
        {
            delete ptr;
        }
    };


std::for_each(aVec.begin(), aVec.end(), DeleteFromVector());
3 голосов
/ 21 мая 2009

Boost lambda уже имеет функтор для удаления последовательностей указателей, кстати:

std::for_each(a.begin(), a.end(), boost::lambda::delete_ptr());
3 голосов
/ 21 мая 2009

Я не знаю, почему у вас происходит сбой, но я думаю, что одна из возможностей заключается в том, что размер вектора не совпадает с размером, который вы передаете. Также я заметил, что вы перебираете от 0 до size-2 , ты не хочешь пройти весь путь до конца?

Один из способов удаления всех элементов в массиве с использованием idiomatic C ++ - это что-то вроде этого:

template<class T>
class deleter
{
  public:
    void operator()(const T* it) const
    {
      delete it;
    }
};

std::for_each(a.begin(), a.end(), deleter<Chromosome>());
2 голосов
/ 21 мая 2009

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

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

1 голос
/ 21 мая 2009

Я нашел проблему.

Это было в самом хорошо спрятанном (никому, кроме глупого старого меня) месте, где это могло быть.

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

Спасибо всем за вашу помощь, я увидел некоторые действительно хорошие практики в этом посте, которым я намерен следовать

1 голос
/ 21 мая 2009

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

printf( "will delete %d\n", (int)c );
delete c;
0 голосов
/ 21 мая 2009

Кажется, что некоторые указатели в вашем коде не ссылаются на правильные объекты Chromosome. Это может произойти, если вы попытаетесь удалить некоторые объекты дважды в результате кода:

Population p;
vector<Chromosome*> chromosomes;
p.clearPool(chromosomes,chromosomes.size()); // You pass by value, so chromosomes is not changed
p.clearPool(chromosomes,chromosomes.size()); // Delete already deleted objects second time

Вы можете найти полезный ptr_vector из Boost Pointer Container Library , чтобы избежать подобных ошибок

0 голосов
/ 21 мая 2009

Я рекомендую использовать умный указатель (т.е. auto_ptr) вместо необработанного указателя и просто использовать метод vector :: clear, который вызовет деструктор для каждого элемента

0 голосов
/ 21 мая 2009
void Population::clearPool(std::vector<Chromosome*>& a)
{
    for(size_t i = 0; i < a.size(); i++) {
        delete a[i];
    }

    a.clear();
}
...