views:

110

answers:

5

Hey, I am working on a drum machine, and am having problems with vectors.

Each Sequence has a list of samples, and the samples are ordered in a vector. However, when a sample is push_back on the vector, the sample's destructor is called, and results in a double free error.

Here is the Sample creation code:

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;
}

And the 'Dynamo' code that creates each sample in the vector:

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));
}

Here is main():

int main()
{
    XDynamo Dynamo;

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

    return 0;
}

And this is what happens when the program is run:

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 ***

However, when the delete [] is removed from the destructor, the program runs perfectly.

What is causing this? Any help is greatly appreciated.

+2  A: 

The compiler added a default copy constructor which means that XSample::Beats got aliased during samples.push_back(...). You should add a copy constructor that initializes XSample correctly, probably by copying XSample::Beats from the argument.

MSN
+2  A: 

vector is copy-constructing your XSamples (using the compiler generated default copy constructor), and as a result, causing problems when it destructs the copy. You can store pointers to XSample in the vector or write a proper copy constructor.

Stephen
why the downvote?
Stephen
+8  A: 

You need a proper copy constructor and assignment operator since you have a non-trivial destructor (more accurately because your class wraps a memory allocation). See the 'Rule of the Big 3':


Update:

As Martin York mentioned in the comments, this answer really just addresses the immediate cause of the problem but doesn't really suggest the best way to fix it, which is to use RAII class members that manage the resources automatically. On the face of it (given the example code), the Beat member might be a std::vector<> instead of a pointer to a manually allocated array. A vector<> member would allow the class to not need a special dtor, copy ctor or assignment operator - all those pieces would be automatically provided for the Beat member if it were a vector<>.

Michael Burr
Or he could allocate a vector of beats.
Martin York
That's a horrible explanation (the wiki link). I would say it is not even good advice. If an object does memory management then unless you are building a smart pointer you have made a bad design decision. An object should be doing a particular job by doing its normal job and doing memory management on one of its members it is actually trying to do 2 jobs and if the user adds another managed object into the class it is seriously difficult to get correct. The simplest solution is to leave memory management to objects explicitly designed for that purpose.
Martin York
+1  A: 

What is happening is that Samples.push_back() copies its argument into the vector. Since XSample doesn't have a copy constructor defined, the compiler creates a default constructor, which does a shallow copy. This means that the Beat pointer in both the original and the copy in the vector point to the same memory. The original is then destructed at the end push_back(), deleting the Beat pointer.

At the end of main, Dynamo is destructed, calling the destructor of each of the elements. This tries to delete the already deleted Beat pointer, resulting in your double delete error.

KeithB
+2  A: 

The problem is you are dynamically allocating memory in your object but not declaring a copy constructor/ assignment operator. When you allocate memory and are responsible for deleting it you need to define all FOUR methods that the compiler generates.

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;
}

What happens to the above when you do:

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

c = a; // Assignment operator called.

Two ways to solve this:

  1. Create the copy constructor/assignment operator.
  2. Use another object that does memory management for you.

I would use solution 2 (as it is simpler).
Its also a better design. Memory management should be done by their own class and you should concentreate on your drums.

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

If you want to do it the hard way:
http://stackoverflow.com/questions/255612/c-dynamically-allocating-an-array-of-objects/255744#255744

If you want to see what the compiler versions look here:
http://stackoverflow.com/questions/1810163/c-copy-constructor-a-class-that-contains-other-objects/1810320#1810320

Martin York