Проблема деструктора C ++ с std :: vector объектов класса - PullRequest
7 голосов
/ 30 апреля 2010

Я не понимаю, как использовать деструкторы, когда у меня есть std :: vector моего класса.

Итак, если я создам простой класс следующим образом:

class Test
{
private:
 int *big;

public:
 Test ()
 {
  big = new int[10000];
 }

    ~Test ()
 {
  delete [] big;
 }
};

Затем в своей основной функции я делаю следующее:

Test tObj = Test();
vector<Test> tVec;
tVec.push_back(tObj);

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

Ответы [ 4 ]

21 голосов
/ 30 апреля 2010

Проблема в том, что вы не определяете конструктор копирования для Test. Таким образом, компилятор создает для вас конструктор копирования по умолчанию, который просто копирует содержимое объекта - в данном случае указатель int.

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

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

Update2: Тривиальным способом обойти все эти ограничения является определение статического массива вместо динамически размещаемого:

class Test
{
private:
  int big[10000];
  // no need for constructors, destructor or assignment operator
};

Однако рекомендуется использовать std::vector<int> вместо массива.

*, то есть содержит указатели на членов с семантикой владения (спасибо @Steve Jessop за разъяснения)

15 голосов
/ 30 апреля 2010

Ваша проблема здесь:

Test tObj = Test();

Test() создает временный объект Test, который затем копируется в tObj. На этом этапе и tObj, и временный объект имеют big для указания на массив. Затем временный объект уничтожается, что вызывает деструктор и уничтожает массив. Поэтому, когда tObj уничтожается, он пытается снова уничтожить уже уничтоженный массив.

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

Вы должны определить конструктор копирования и оператор присваивания, чтобы при копировании объекта Test массив big копировался или имел какой-то счетчик ссылок, чтобы он не уничтожался до тех пор, пока все владельцы не будут уничтожены. уничтожены.

Простое решение - определить класс следующим образом:

class Test
{
private:
 std::vector<int> big;

public:
 Test (): big(10000) {}
};

В этом случае вам не нужно определять какой-либо деструктор, конструктор копирования или оператор присваивания, потому что член std::vector<> позаботится обо всем. (Но учтите, что это означает, что при копировании экземпляра Test. Выделяется и копируется 10 000 байт.)

0 голосов
/ 30 апреля 2010
Test tObj = Test();

Это неправильно и должно быть, поскольку оно не создает копии:

Test tObj;

Это также создает много копий:

 vector<Test> tVec;
 tVec.push_back(tObj);

Так что, если вы освободите один массив int, вы освободите все массивы. И следующее удаление не удастся.

Вам нужно либо:

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

  • Зачем использовать указатель?

class Test    
{   
private:  
  int big[10000];    
public:

};

Это будет хорошо работать.

0 голосов
/ 30 апреля 2010

Без конструктора копирования вектор создаст плоскую копию вашего объекта.Это приводит к двум объектам типа Test, ссылающимся на один и тот же массив big.Первый экземпляр удаляет массив при его уничтожении, а затем второй экземпляр пытается разыменовать удаленный указатель, что является неопределенным поведением.

...