2010-05-11 3 views
2

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

Каждая последовательность имеет список образцов, а образцы упорядочены в векторе. Однако, когда образец является 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 *** 

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

Что вызывает это? Любая помощь приветствуется.

+1

Почему вы не просто использовать ' вектор' в 'XSample'? – GManNickG

+0

Согласитесь, 'Beat' должен быть вектором. – Bill

ответ

3

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

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() 
{ 
} 

Если вы хотите сделать это трудный путь:
Dynamically allocating an array of objects

Если вы хотите увидеть, какие версии компилятора смотрите здесь:
C++ implicit copy constructor for a class that contains other objects

2

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

2

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

8

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


Update:

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

+0

Или он мог бы выделить вектор ударов. –

+0

Это ужасное объяснение (ссылка wiki). Я бы сказал, что это даже не хороший совет. Если объект управляет памятью, тогда, если вы не строите умный указатель, вы сделали плохое дизайнерское решение. Объект должен выполнять определенную работу, выполняя свою обычную работу и делая управление памятью на одном из своих членов, он на самом деле пытается выполнить 2 задания, и если пользователь добавляет в класс другой управляемый объект, это очень сложно получить правильно. Самое простое решение - оставить управление памятью объектами, явно предназначенными для этой цели. –

+0

Человек, который я могу иметь в виду, когда в плохом настроении. Сожалею. –

1

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^