C++, путаница с конструктором копирования

скажем, я создал собственный класс Array и имею следующий конструктор:

    Array::Array(size_t size)
    {
      mySize = size;
      //myData is of type int*
      myData = new int[mySize]; // this stuff is allocated on a heap
    }

Насколько мне известно, конструктор копирования по умолчанию в этом случае будет реализован следующим образом:

    Array::Array(Array& a)
    {
      mySize = a.mySize;
      myData = a.myData; //points to the same memory on the heap
    }

Наконец, скажем, у меня есть следующий код в моем файле main.cpp

     int main()
     {
       Array a(1);
       Array b(a); //copy constructor is invoked
     }

То, что я ожидал, было утечкой памяти из-за удаления указателей myData на свободную память хранилища, НО у меня есть следующая ошибка времени выполнения:

*** glibc detected *** ./main.out: double free or corruption (fasttop): 0x086ad008 ***

ПОЧЕМУ? Кажется, что ~Array() каким-то образом автоматически освобождает память, выделенную в куче, но мне это очень нелогично. Может быть, я что-то упускаю?

ОБНОВИТЬ:

      class Array{
         private:
           size_t mySize;
           int *myData;
           ...

ОБНОВЛЕНИЕ 2:

основной.cpp:

   #include <iostream>
   #include "array.h"

   int main()
   {
     Array a(1);
     Array b(a);
   }

массив.ч:

   #ifndef ARRAY_H_
   #define ARRAY_H_
   #include <stddef.h>
   class Array{
   private:
       size_t mySize;
       int *myData;
   public:
       Array(size_t size);
       ~Array();
       void set(int i,int val);
       int get(int i);
       size_t getSize();
   };
   #endif

массив.cpp:

   #include "array.h"

   Array::Array(size_t size)
   {
     mySize = size;
     myData = new int[mySize];
   }

   Array::~Array()
   {
     delete[] myData;
   }

   void Array::set(int i, int val)
   {
     if(i>=0 && i<mySize)
       myData[i] = val;
   }

   int Array::get(int i)
   {
     if(i>=0 && i<mySize)
       return myData[i];
     else return -1;
   }
   size_t Array::getSize()
   {
     return mySize;
   }

person Vladislavs Burakovs    schedule 06.03.2013    source источник
comment
Вы говорите, что ваш деструктор не освобождает память? Если да, то трудно понять ошибку.   -  person juanchopanza    schedule 07.03.2013
comment
возможный дубликат Что такое правило трех?   -  person ildjarn    schedule 07.03.2013
comment
Я говорю, что деструктор предположительно освобождает память, которая указывает на кучу, чего не должно быть... по крайней мере, насколько я знаю.   -  person Vladislavs Burakovs    schedule 07.03.2013
comment
Вы правы, этого не должно быть. Вы живете в альтернативной вселенной.   -  person Edward Strange    schedule 07.03.2013
comment
Деструктор не будет делать ничего подобного, пока вы не скажете ему об этом. Можете ли вы опубликовать sscce?   -  person juanchopanza    schedule 07.03.2013
comment
Не могли бы вы опубликовать весь класс? Вы определили оператор присваивания? деструктор? пользовательская реализация delete? Является ли Array подклассом?   -  person D.Shawley    schedule 07.03.2013


Ответы (5)


Я думаю, что в вашем деструкторе у вас есть

 Array::~Array(void)
 {
      delete [] myData; //points to the same memory on the heap
 }

Проблема вдвойне свободна

Пример:

int main()
{
       Array a(1);  // here a.myData = 0x12345678
       Array b(a); //copy constructor is invoked // here b.myData = 0x12345678

       // pop  stack (call destroy of object)
       // delete b.myData
       // delete a.myData already delete
}

Двойной бесплатный

EDIT: для вашего конструктора копирования используйте const, потому что вы не изменяете a.

 Array::Array(const Array& a)
 {
      mySize = a.mySize;
      myData = a.myData; //points to the same memory on the heap
 }

Удачи !

person Quentin Perez    schedule 06.03.2013
comment
Спасибо, я совсем забыл про оператор delete[]. Итак, должно быть так, что delete[] автоматически вызывается в деструкторе. - person Vladislavs Burakovs; 07.03.2013
comment
@ВладиславБураков Нет, он не вызывается автоматически. - person juanchopanza; 07.03.2013

Итак... несмотря на ваши утверждения, оказывается, что вы удаляете массив в деструкторе, а конструктор копирования является поверхностной копией, поэтому вы получаете двойное удаление. Простой.

Array::~Array()
{
  delete[] myData;
}

Поскольку это динамический массив, он должен владеть данными, поэтому вы имеете право удалить в деструкторе, но вам нужно «глубоко» скопировать в конструкторе копирования и оператор присваивания. См. правило трех.

person juanchopanza    schedule 06.03.2013
comment
вам нужно глубокое копирование - или подсчет ссылок, или отключение копирования. - person Cheers and hth. - Alf; 07.03.2013
comment
@Cheersandhth.-Alf Хорошо, но я полагаю, что простой динамический массив должен иметь простую семантику владения данными. И быть копируемым и назначаемым. - person juanchopanza; 07.03.2013

Ваша копия является поверхностной копией, поэтому ЕСЛИ у вас есть деструктор, освобождающий память, каждый объект пытается удалить одну и ту же память.

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

Мне нравится предложение Google отключить конструкторы копирования и назначения и предпочитаю явные CopyFrom() методы.

person Community    schedule 06.03.2013

«двойное освобождение» означает, что один и тот же указатель был передан delete[] дважды. потому что в вашем деструкторе (предположительно) он delete[]-ed как в объекте a, так и в b. практическое решение: используйте std::vector, не возитесь с необработанными массивами и т. д. без необходимости.

person Cheers and hth. - Alf    schedule 06.03.2013
comment
О, так может быть дело в том, что delete[] вызывается автоматически в деструкторе? - person Vladislavs Burakovs; 07.03.2013
comment
@ВладиславБураков: нет, это не вызывается автоматически. предположительно вы или кто-то, кто написал код, поместил его туда. трудно сказать, так как вы забыли представить этот код. - person Cheers and hth. - Alf; 07.03.2013
comment
Нет, остальное я не представил, потому что ничего кроме конструктора я не переопределял - person Vladislavs Burakovs; 07.03.2013
comment
@ВладиславБураков: хм, информация, которую вы дали, не соответствует действительности. Не могли бы вы представить небольшой полный пример, демонстрирующий описанное поведение. - person Cheers and hth. - Alf; 07.03.2013
comment
Вы сказали дважды, что считаете, что delete[] вызывается автоматически, и вам сказали, что вы ошибаетесь. Можете ли вы опубликовать полный код класса Array или полный небольшой пример, показывающий эту ошибку? - person Nik Bougalis; 07.03.2013

Я думаю, что ваши деструкторы (из массива «a» и «b») пытаются освободить одну и ту же память (двойное освобождение). Может быть, вам стоит проверить свои данные перед освобождением.

person FELIPE_RIBAS    schedule 06.03.2013