2

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.

4

5 回答 5

8

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

于 2010-05-11T20:00:54.533 回答
4

问题是您在对象中动态分配内存,但没有声明复制构造函数/赋值运算符。当您分配内存并负责删除它时,您需要定义编译器生成的所有四个方法。

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++ 包含其他对象的类的隐式复制构造函数

于 2010-05-11T22:32:33.447 回答
2

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.

于 2010-05-11T20:00:41.150 回答
2

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.

于 2010-05-11T20:00:44.767 回答
1

正在发生的事情是将Samples.push_back()其参数复制到向量中。由于XSample没有定义复制构造函数,编译器创建了一个默认构造函数,它执行浅拷贝。这意味着Beat向量中原始和副本中的指针都指向同一内存。然后在末尾销毁原始文件push_back(),删除Beat指针。

在 main 的末尾,Dynamo被破坏,调用每个元素的析构函数。这会尝试删除已删除的Beat指针,从而导致您的双重删除错误。

于 2010-05-11T20:19:00.233 回答