Double Free внутри деструктора при добавлении в вектор - PullRequest
2 голосов
/ 11 мая 2010

Эй, я работаю на драм-машине и у меня проблемы с векторами.

Каждая последовательность имеет список образцов, и образцы упорядочены в векторе. Тем не менее, когда выборкой является push_back для вектора, вызывается деструктор выборки, что приводит к двойной двойной ошибке.

Вот пример кода создания:

class XSample
{
  public:
    Uint8 Repeat;
    Uint8 PlayCount;
    Uint16 Beats;
    Uint16 *Beat;
    Uint16 BeatsPerMinute;

    XSample(Uint16 NewBeats,Uint16 NewBPM,Uint8 NewRepeat);
    ~XSample();

    void GenerateSample();

    void PlaySample();
};

XSample::XSample(Uint16 NewBeats,Uint16 NewBPM,Uint8 NewRepeat)
{
    Beats = NewBeats;
    BeatsPerMinute = NewBPM;
    Repeat = NewRepeat-1;
    PlayCount = 0;

    printf("XSample Construction\n");
    Beat = new Uint16[Beats];
}

XSample::~XSample()
{
    printf("XSample Destruction\n");
    delete [] Beat;
}

И код «Динамо», который создает каждый образец в векторе:

class XDynamo
{
  public:
    std::vector<XSample> Samples;

    void CreateSample(Uint16 NewBeats,Uint16 NewBPM,Uint8 NewRepeat);
};

void XDynamo::CreateSample(Uint16 NewBeats,Uint16 NewBPM,Uint8 NewRepeat)
{
    Samples.push_back(XSample(NewBeats,NewBPM,NewRepeat));
}

Вот главная ():

int main()
{
    XDynamo Dynamo;

    Dynamo.CreateSample(4,120,2);
    Dynamo.CreateSample(8,240,1);

    return 0;
}

И вот что происходит при запуске программы:

Starting program: /home/shawn/dynamo2/dynamo 
[Thread debugging using libthread_db enabled]
XSample Construction
XSample Destruction
XSample Construction
XSample Destruction
*** glibc detected *** /home/shawn/dynamo2/dynamo: double free or corruption (fasttop): 0x0804d008 ***

Однако, когда delete [] удаляется из деструктора, программа работает отлично.

Что вызывает это? Любая помощь с благодарностью.

Ответы [ 5 ]

8 голосов
/ 12 мая 2010

Вам нужен правильный конструктор копирования и оператор присваивания, так как у вас нетривиальный деструктор (точнее, потому что ваш класс оборачивает выделение памяти).См. «Правило Большой 3»:


Обновление:

Как отметил Мартин Йорк в комментариях,этот ответ на самом деле просто устраняет непосредственную причину проблемы, но на самом деле не предлагает лучший способ ее устранения, а именно использование членов класса RAII, которые автоматически управляют ресурсами.На первый взгляд (с учетом примера кода) член Beat может быть std::vector<> вместо указателя на массив, выделенный вручную.Член vector<> позволил бы классу не требовать специального dtor, copy ctor или оператора присваивания - все эти части были бы автоматически предоставлены для члена Beat, если бы это был vector<>.

3 голосов
/ 12 мая 2010

Проблема в том, что вы динамически распределяете память в своем объекте, но не объявляете конструктор копирования / оператор присваивания. Когда вы выделяете память и отвечаете за ее удаление, вам необходимо определить все четыре метода, которые генерирует компилятор.

class XSample
{
    public:
        // Pointer inside a class.
        // This is dangerous and usually wrong.
        Uint16 *Beat;
};

XSample::XSample(Uint16 NewBeats,Uint16 NewBPM,Uint8 NewRepeat)
{
    // You allocated it here.
    // But what happens when you copy this object or assign it to another variable.
    Beat = new Uint16[NewBeats];
}

XSample::~XSample()
{
    // Delete here. Turns into double delete if you don't have
    // copy constructor or assignment operator.
    delete [] Beat;
}

Что происходит с вышеизложенным, когда вы делаете:

XSample   a(15,2,2);
XSample   b(a);  // Copy constructor called.
XSample   c(15,2,2);

c = a; // Assignment operator called.

Два способа решить эту проблему:

  1. Создать конструктор копирования / оператор присваивания.
  2. Используйте другой объект, который выполняет управление памятью для вас.

Я бы использовал решение 2 (как оно проще).
Это также лучший дизайн. Управление памятью должно осуществляться их собственным классом, и вы должны сосредоточиться на своих барабанах.

class XSample
{
  public:
    std::vector<Uint16> Beat;
};

XSample::XSample(Uint16 NewBeats,Uint16 NewBPM,Uint8 NewRepeat):
        Beat(NewBeats)
{
         // Notice the vector is constructed above in the initializer list.
}

    // Don't need this now.
XSample::~XSample()
{
}

Если вы хотите сделать это трудным путем:
Динамическое выделение массива объектов

Если вы хотите посмотреть, как выглядят версии компилятора:
C ++ неявный конструктор копирования для класса, который содержит другие объекты

2 голосов
/ 12 мая 2010

vector копирует ваши XSample s (используя сгенерированный компилятором конструктор копирования по умолчанию) и, как результат, вызывает проблемы, когда он уничтожает копию. Вы можете хранить указатели на XSample в векторе или написать правильный конструктор копирования.

2 голосов
/ 12 мая 2010

Компилятор добавил конструктор копирования по умолчанию, что означает, что XSample::Beats получил псевдоним во время samples.push_back(...). Вы должны добавить конструктор копирования, который правильно инициализирует XSample, возможно, путем копирования XSample::Beats из аргумента.

1 голос
/ 12 мая 2010

В результате Samples.push_back() копирует свой аргумент в вектор. Поскольку XSample не имеет определенного конструктора копирования, компилятор создает конструктор по умолчанию, который выполняет поверхностное копирование. Это означает, что указатель Beat в оригинале и в векторе указывает на одну и ту же память. Оригинал затем уничтожается в конце push_back(), удаляя указатель Beat.

В конце main, Dynamo разрушается, вызывая деструктор каждого из элементов. Это попытается удалить уже удаленный указатель Beat, что приведет к вашей двойной ошибке удаления.

...