删除堆上存储堆上数据的对象

时间:2014-03-19 17:38:43

标签: c++ memory-management heap-memory heap-corruption

我的程序是使用SDL库中的类编写的。

我有以下课程:

class s_group
{
    private:
        SDL_Surface* image;
        unsigned int* F_total;
        float* F_length;
        SDL_Rect** F;
        float* F_current;
        unsigned int S_total;
        unsigned int S_current;

    public:
        s_group(void);
        virtual ~s_group(void);

        bool setup( const char* filename, unsigned int s );
        //other member functions
};

私有成员指针每个都存储在堆上声明的内存位置,由成员函数setup分配。

bool s_group::setup( const char* filename, unsigned int s )
{
    s_group::~s_group();//delete already allocated heap memory
    if(!load_file(image, filename))
    {
        image = NULL;
        return false; 
    }

    S_total = s;
    F = new SDL_Rect*[S_total];
    F_total = new unsigned int[S_total];
    F_length = new float[S_total];
    F_current = new float[S_total];

    for(unsigned int index = 0; index < S_total; ++index)
    {
        F[index] = NULL;
        F_total[index] = 0;
        F_length[index] = 0.f;
        F_current[index] = 0.f;
    }
    //loop for each array slot and set values of data
    return true;
}

在一个大函数中,我在堆上创建了这个类的对象,将其地址存储在名为s_group的{​​{1}}指针中。

sparkle

完成该函数后,我调用s_group* sparkle = new s_group; sparkle->setup("sparkle_final.png", 1 ); 来重新分配堆内存。删除此行可以解决问题,但是会出现内存泄漏。

delete

这将调用类的析构函数,我认为由于delete sparkle; sparkle = NULL; 运算符的内部使用而导致错误发生。

delete

在到达删除操作符时,会出现一个对话框,说明:

  

Windows在Program.exe中触发了断点。这可能是由于堆的损坏,这表明Program.exe或它已加载的任何DLL中存在错误。

如何在不导致堆损坏的情况下s_group::~s_group(void) { SDL_FreeSurface(image); image = NULL; for(unsigned int s = 0; s < S_total; ++s) { for(unsigned int f = 0; f < F_total[s]; ++f) { F[s][f].x = 0; F[s][f].y = 0; F[s][f].w = 0; F[s][f].h = 0; } delete[] F[s]; F[s] = NULL; } delete[] F; F = NULL; delete[] F_total; F_total = NULL; delete[] F_length; F_length = NULL; delete[] F_current; F_current = NULL; S_total = 0; S_current = 0; } 此对象?

3 个答案:

答案 0 :(得分:2)

来自effective C++ Scott Meyers

第9项:在施工或销毁期间切勿调用虚拟功能。

  

你不应该在构造或破坏期间调用虚函数,因为调用不会按照你的想法进行,如果有的话,你仍然会不高兴。如果你是一个正在恢复的Java或C#程序员,请密切关注这个项目,因为这是这些语言的地方,而C ++ zags。

实际上,即使你应该定义你的析构函数,强行调用它也应该是不可能的

答案 1 :(得分:0)

我无法编译你的代码但是这里......

我注意到的第一件事是你打电话给你的析构函数..你不想这样做!相反,创建一个释放函数并调用它。

我注意到的下一件事是类本身内没有FRAME变量..所以这一行:

FRAME = new SDL_Rect*[S_total];

将导致编译错误,并且析构函数使用FRAME但不存在此类变量。我认为您打算将其更改为F,因为如果没有,那么这一行:

F[index] = NULL;

是未定义的行为,因为F未初始化..

此外,您从未初始化FRAME的每个索引,因此在析构函数中访问它,如:

FRAME[s][f].x = 0;

是禁忌。

再次,你打电话

delete[] F; 
F = NULL;

但是F没有分配内存并且未初始化。

因此我想到了所有的补丁:

class s_group
{
    private:
        SDL_Surface* image;
        unsigned int* F_total;
        float* F_length;
        SDL_Rect** FRAME;
        float* F_current;
        unsigned int S_total;
        unsigned int S_current;

        void Release();

    public:
        s_group(void);
        virtual ~s_group(void);

        bool setup(const char* filename, unsigned int s);
        //other member functions
};


bool s_group::setup(const char* filename, unsigned int s)
{
    Release();//delete already allocated heap memory

    if(!load_file(image, filename))
    {
        image = NULL;
        return false; 
    }

    S_total = s;
    FRAME = new SDL_Rect*[S_total];
    F_total = new unsigned int[S_total];
    F_length = new float[S_total];
    F_current = new float[S_total];

    for(unsigned int index = 0; index < S_total; ++index)
    {
        FRAME[index] = NULL;
        F_total[index] = 0;
        F_length[index] = 0.f;
        F_current[index] = 0.f;
    }
    //loop for each array slot and set values of data
    return true;
}

void s_group::Release()
{
    SDL_FreeSurface(image);
    image = NULL;

    for(unsigned int s = 0; s < S_total; ++s)
    {
        for(unsigned int f = 0; f < F_total[s]; ++f)
        {
            if (FRAME[s])
            {
                FRAME[s][f].x = 0; 
                FRAME[s][f].y = 0;
                FRAME[s][f].w = 0; 
                FRAME[s][f].h = 0;
            }
        }
        delete[] FRAME[s]; 
        FRAME[s] = NULL;
    }
    delete[] FRAME; 
    FRAME = NULL;

    delete[] F_total; 
    F_total = NULL;

    delete[] F_length; 
    F_length = NULL;

    delete[] F_current; 
    F_current = NULL;

    S_total = 0;
    S_current = 0;
}

s_group::~s_group(void)
{
    Release();
}

应该这样做..只是不要忘记为FRAME[index]分配内存我不知道你要分配多少或什么,所以我将Release功能更改为使用FRAME[index]

检查if-statement是否有效

我强烈建议你使用一些SmartPointers并忘记自己处理每一个内存分配..

答案 2 :(得分:0)

自发布此问题以来,我找到了错误的来源并解决了问题。 在为动态2D数组设置数据值的单独代码部分中,循环验证不正确。

for( unsigned int index = 0; index <= F_total[ S_current ]; ++index ) {  
    //set data values for each slot in the array
    F[ S_current ][ index ].x = 0; etc...
}

可以看出,循环显然会尝试修改与创建的数组大小相等的位置。当然注意到数组从索引0开始,所以最后的插槽将是大小 - 1.编写代码时我错过了一些非常愚蠢的东西。实际循环:

for( unsigned int index = 0; index < F_total[ S_current ]; ++index ) {  
    //set data values for each slot in the array
    F[ S_current ][ index ].x = 0; etc...
}

任何人尝试自己的内存管理的消息:

  • 查找堆损坏的来源很困难,因为编译器会在代码的各个部分找到错误,但不一定会导致问题。
  • 问题的原因只会出现在影响内存的代码部分。确保您不会尝试访问或更糟糕地修改您未获得的任何内存。
  • 我仍然相信内存管理是一种很好的学习方式,而不是像建议那样使用容器或智能指针来完成任何项目。这是我个人的偏好,尽管自定义内存管理通常只提供很少的优点,只有复杂性。
  • 在寻求帮助时,请提供有关问题的所有相关代码。虽然编译器可能会引导您解决一个部分中的问题,正如我之前所说的,堆损坏并不一定存在。