Деструктор на массиве объектов, которые уже были разрушены - PullRequest
1 голос
/ 22 марта 2019

Я немного новичок в Objects в C ++, и у меня есть следующая упрощенная проблема:

Я хочу создать массив объектов, которые уже инициализированы конструктором класса.

Таким образом:

int main() {

 Test A(1);
 Test B(2);
 Test C(3);
 Test TestArray[3]={A,B,C};

 /*
  Code that both uses A,B,C directly and TestArray
 */

 return 0;
}

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

Таким образом:

class Test {
    int *PointerToDynMem;
public:
    Test(int);
    ~Test();
};

Test::Test(int a){
    PointerToDynMem=new int(a);
}

Test::~Test(){
    delete PointerToDynMem;
}

Я думаю, что происходит, когда программа заканчивается A, B и C выходят изсфера и вызов деструктора.Но, похоже, что и когда TestArray выходит из области видимости, он также вызывает деструктор, но A, B, C уже были освобождены soo.ОШИБКА.

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

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

Ответы [ 3 ]

4 голосов
/ 22 марта 2019

TestArray[3]={A,B,C}; скопирует int*, хранящиеся в A, B и C, используя конструктор копирования по умолчанию.Чтобы исправить это, создайте все конструкторы и операторы, которые иначе были бы созданы для вас.Прочитайте правило трех / пяти / нуля .

class Test {
    int *PointerToDynMem;
public:
    Test(int);
    Test(const Test&);            // implement this
    Test(Test&&);                 // implement this
    Test& operator=(const Test&); // implement this
    Test& operator=(Test&&);      // implement this
    ~Test();
};
2 голосов
/ 22 марта 2019

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

Массив является , а не указателем. Если бы это было так, у нас были бы только указатели. Имя массива может и действительно затухает до указателя на первый элемент в массиве (что может сильно раздражать), но это не указатель. Это объект, который хранит N объекты в непрерывном фрагменте памяти, и информация о размере является частью его типа. Это означает, что int[5] отличается от типа int[6].

Что здесь происходит, это

Test TestArray[3]={A,B,C};

создает массив из трех Test, а затем копирует инициализирует каждый Test объект в массиве из инициализаторов {A,B,C}. Поэтому, когда main заканчивается, TestArray уничтожается, вызывайте деструктор для каждого элемента. А затем C, B и A как уничтоженные в этом порядке.

Это проблема для вас, так как ваш класс просто использует конструктор копирования по умолчанию. Это означает, что каждый объект в массиве имеет копию точки объекта, которой он был инициализирован, и указывает на ту же память. Поэтому, когда массив уничтожается, delete вызывается во всех указателях-членах, а затем C, B и A снова пытаются удалить ту же самую память, что является неопределенным поведением.

Что вам нужно сделать, это следовать правилу из трех и создать специальные функции-члены, чтобы ваш класс копировался правильно, или вы можете использовать RAII в форме интеллектуальных указателей / std::vector и пусть они справятся со всем этим для вас, и вы можете использовать правило нуля

1 голос
/ 22 марта 2019

Для этого кода вам нужно добавить конструктор копирования (а также оператор = и т.д. для «реального» кода)

Test::Test(const Test & t) {
  PointerToDynMem=new int(*t.PointerToDynMem);
}

иначе ваш int* является общим (TestArray[0].PointerToDynMem равен A.PointerToDynMem, TestArray[1].PointerToDynMem равен B.PointerToDynMem и TestArray[2].PointerToDynMem равен C.PointerToDynMem) и удален 2 раза

...