1

所以我有这个二维动态数组,当我完成它时我想释放它的内容。但是,在析构函数之后,我一直遇到堆损坏。如果我注释掉析构函数,代码可以正常工作(当然有内存泄漏)。(视觉工作室 2005)

FrameData::FrameData(int width, int height)
{
    width_ = width;
    height_ = height;

    linesize[0] = linesize[1] = linesize[2] = linesize[3] = 0;

    // Initialise the 2d array
    // Note: uint8_t is used by FFMPEG (typedef unsigned char uint8_t)
    red = new uint8_t* [height];
    green = new uint8_t* [height];
    blue = new uint8_t* [height];

    for (int i=0; i < height; i++)
    {
        red[i] = new uint8_t [width];
        green[i] = new uint8_t [width];
        blue[i] = new uint8_t [width];
    }       
}

FrameData::~FrameData()
{

    // Delete each column
    for (int i=0; i < height_; i++)
    {           
        delete[] ((uint8_t*) red[i]);
        delete[] ((uint8_t*)green[i]);
        delete[] ((uint8_t*)blue[i]);       
    }

    // Final cleanup
    delete[] red;
    red = NULL;

    delete[] green;
    green = NULL;

    delete[] blue;
    blue = NULL;    
} 

我不知道代码有什么问题。唯一的另一件事是在其他地方,我在发生崩溃的循环中这样做了

FrameData myFrame;
std::vector<FrameData> frames;
...snipped...
frames.push_back(myFrame);

这应该不会造成任何问题,对吧?如果我没记错的话, push_back 会创建一个副本,而不是存储指针或引用。

PS。是的,我应该使用向量。但我不被允许。

附加信息:

未定义 operator= 和复制构造函数。我想这是问题的原因。

4

4 回答 4

5

你的问题是你在这里猜到的:

FrameData myFrame;
std::vector<FrameData> frames;
...snipped...
frames.push_back(myFrame);

向量会复制您推入的元素。您的复制构造函数和/或operator=您的类有什么?如果您没有定义,编译器为您创建的默认版本只会复制您的类的成员。这会将指针 members 复制redgreenblue实例。然后,您复制的旧实例将在超出范围时被销毁,从而导致指针被删除。您复制到向量中的那个将具有无效的指针,因为指针的目标因此被删除。

一个好的经验法则是,如果您有任何原始指针成员,那么您需要创建一个复制构造函数,operator=它可以正确处理这种情况,方法是确保指针被赋予新值而不是共享,或者所有权被转移实例之间。

例如,std::auto_ptr该类有一个原始指针——复制构造函数的语义是将指针的所有权转移到目标。

该类boost::shared_ptr有一个原始指针——语义是通过引用计数来共享所有权。这是处理std::vectors包含指向您的类的指针的好方法 - 共享指针将为您控制所有权。

另一种方法可能是使用向量来代替您的成员指针 - 无论如何,成员指针只是您的数组的别名,因此向量是一个很好的替代品。

于 2009-08-06T08:30:09.367 回答
1

这不是您问题的答案,只是一个观察。

由于您的帧数据可能很大,为避免过度复制,您最好使用

std::vector<FrameData *> frames;

编辑:正如其他人指出的那样,这也将解决您的崩溃问题。

于 2009-08-06T08:37:53.440 回答
1

除非您有 FrameData 类的深拷贝构造函数和赋值运算符,否则我的直觉是编译器会生成一个拷贝构造函数以与 push_back 一起使用。自动生成的复制构造函数和赋值操作符将进行成员复制,这将在这种情况下产生浅拷贝。不幸的是,您的析构函数不知道副本,因此在复制过程中,很有可能 FrameData 的临时副本被销毁,这将带走您的所有数据。

稍后在该过程中再次调用析构函数将导致双重释放,另外其他分配可能已经使用了部分“空闲”内存。从这里看,这似乎是堆损坏的一个很好的理由。

发现此类问题的最佳方法通常是使用 ValGrind 或 Purify 等工具来查明问题。

于 2009-08-06T08:35:28.607 回答
0

您对 push_back 进行复制是正确的,但是 FrameData 是否有合适的复制构造函数和赋值运算符?

另外,为什么演员在这里:

delete[] ((uint8_t*) red[i]);

在 C++ 中,如果您必须使用 C 风格(或重新解释)强制转换,那么代码几乎肯定是错误的。

于 2009-08-06T08:27:39.077 回答