Visual Studio 2010 C ++ ошибка во время выполнения - PullRequest
4 голосов
/ 06 мая 2011

Я столкнулся со странным поведением в компиляторе Visual Studio 2010 C ++.Следующий код компилируется, но выдает «Ошибка отладки» после выполнения с сообщением:

«_ BLOCK_TYPE_IS_VALID (pHead-> nBlockUse)» *

Компилируется и работает в GCC.Это моя вина?

#include <iostream>
#include <vector>


using namespace std;

typedef unsigned int uint;


class Foo {
    vector<int*> coll;
public:

    void add(int* item) {
       coll.push_back(item);
    }

    ~Foo() {
        for (uint i = 0; i < coll.size(); ++i) {
            delete coll[i];
            coll[i] = NULL;
        }
    }
};

int main()
{
   Foo foo;
   foo.add(new int(4));
   Foo bar = foo;

   return 0;
}

Ответы [ 4 ]

7 голосов
/ 06 мая 2011

Вы не реализовали конструктор копирования и оператор назначения копирования (см. Правило трех).Это приводит к неглубокой копии указателей в вашем векторе, вызывая двойное удаление и утверждение.РЕДАКТИРОВАНИЕ: Двойное удаление - неопределенное поведение, поэтому и VS, и gcc здесь верны, им разрешено делать все, что они захотят.

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

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

РЕДАКТИРОВАТЬ: Еще одно примечание о вашем интерфейсе:Подобные интерфейсы, которые передают владение передаваемыми указателями, могут вызвать путаницу у людей, использующих ваш классЕсли кто-то передал адрес int, не выделенный в куче, то ваш деструктор все равно потерпит неудачу.Лучше либо принять по значению, если это возможно, либо клонировать переданный элемент, сделав собственный вызов new в функции add.

3 голосов
/ 06 мая 2011

Вы удаляете элемент дважды, потому что строка

Foo bar = foo;

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

2 голосов
/ 06 мая 2011

Проблема в том, что элемент вектора bar и foo элемента одинаков.Когда foo выходит из области видимости, вызывается деструктор, который освобождает указатель, оставляя висячий элемент вектора bar. Деструктор bar пытается освободить свой векторный элемент, который остался висеть, и вызывает ошибку времени выполнения.Вы должны написать конструктор копирования.

Foo bar = foo; // Invokes default copy constructor.

Редактировать 1: Посмотрите на эту ветку, чтобы узнать о Правиле трех

0 голосов
/ 06 мая 2011

Более простое решение - не использовать int*.

#include <iostream>
#include <vector>


using namespace std;

typedef unsigned int uint;


class Foo {
    vector<int> coll; // remove *
public:

    void add(int item) { // remove *
       coll.push_back(item);
    }

    // remove ~Foo
};

int main()
{
   Foo foo;
   foo.add(4); // remove `new` call
   Foo bar = foo;

   return 0;
}

В общем, старайтесь избегать new.

Если вы не можете, используйте умный менеджер (например, std::unique_ptr) для очистки памяти.

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

...