c ++正确删除结构和指针

时间:2015-10-22 07:48:16

标签: c++ pointers memory struct garbage-collection

我有一个关于如何正确删除结构的问题,以及它在内部声明的各个指针。

我从已经运行的项目中提取了一个示例,它似乎无法正常工作,代码不会崩溃,但似乎我有一些“内存泄漏”。我不确定这是正确的措辞。问题是这些值并没有真正重置,并且在我下次启动课程时会保留在内存中。

下面的Sudocode:

标题

ProgramHeader.h

class ClassA : public publicClassA
{
    public:
        ClassA(void);

        virtual ~ClassA();

    private:

    struct ApStruct{
            struct
            {
                float *refA[2];
                float *refB[2];
                float *pVarA;
            } fR;

            struct
            {
                float *refA[2];
                float *refB[2];
                float *pVarA;
            } f1kHz;
        };
        ApStruct* GetApStruct;  
}

程序:

Program.cpp

#include "ProgramHeader.h"

ClassA::~ClassA()
{
    //EDIT i did a typo my looks like this:
    //delete ApStruct; //Wrong code
    delete GetApStruct; //Corrected - however still not working
}

main()
{
    GetApStruct  = new ApStruct();

    //Do Code
}

希望这一切都有点意义,

修改 我在代码中更新了一行错误 - 然而问题仍然是一样的。在实施解决方案之前,我将在下面了解一下。

编辑24/10/2015 我一直在尝试下面的一些建议,但我无法找到解决问题的办法,我必须承认我也很难将其缩小范围,这可能会导致问题。

我的代码是DLL的一部分。代码包装了一些我无法控制的源代码,因此我有限的选项如何使用构造函数和新的指针初始化。

我仍然认为我有内存泄漏问题的原因是,如果我在我的代码中添加“魔术浮点数”,我的函数输出会发生变化,即使浮点数 在任何地方使用 - 它只是宣布。

我在以下情况下获得了不同的结果:

  1. 调用InitCode - 一次!
  2. 然后我会多次调用CallCode - 进行我的计算
  3. 破坏类的实例
  4. 当我再次重复上述内容时,我从第一次运行代码时获得了不同的结果,但之后它保持不变。

    如果我包含魔术线似乎都有效???

    更新了SudoCode:

    Program.cpp
    
    #include "ProgramHeader.h"
    
    ClassA::~ClassA()
    {
        //EDIT i did a typo my looks like this:
        //delete ApStruct; //Wrong code
        delete GetApStruct; //Corrected - however still not working
    }
    
    main()
    {
        void initCode()
        {
            GetApStruct  = new ApStruct();
    
            float InitValue = 0.F
    
            //Magic line:
            float magicLine = 123456.f; //If this line is commented out i get different results in my code
            //End Magic Line
    
            fr.refA[0] = &InitValue;
            fr.refA[0] = &InitValue;
            fr.refA[0] = &InitValue;
            fr.pVarA   = &InitValue;
            ... 
        }
    
        void CallCode()
        {
            float CallValue = 123.F
    
            //Magic line:
            float magicLine = 123456.f; //If this line is commented out i get different results in my code
            //End Magic Line
    
            fr.refA[0] = &CallValue;
            fr.refA[0] = &CallValue;
            fr.refA[0] = &CallValue;
            fr.pVarA   = &CallValue;
            ...
        }
    }
    

    谢谢你的支持,

    托马斯

4 个答案:

答案 0 :(得分:1)

我建议使用以下内容进行分配和清理......

#include <iostream>

using namespace std;

class ClassA 
{
public:
    ClassA(void);
    virtual ~ClassA();

private:
    struct ApStruct {
        struct
        {
            float *refA[2];
            float *refB[2];
            float *pVarA;
        } fR;

        struct
        {
            float *refA[2];
            float *refB[2];
            float *pVarA;
        } f1kHz;
    };
    ApStruct* GetApStruct;
};

ClassA::ClassA(void) {
    GetApStruct = new ApStruct{};
    GetApStruct->fR.refA[0] = new float{ 1.f };
    GetApStruct->fR.refA[1] = new float{ 2.f };
    GetApStruct->fR.refB[0] = new float{ 3.f };
    GetApStruct->fR.refB[1] = new float{ 4.f };
    GetApStruct->fR.pVarA = new float { 0.f };
    // do same for struct f1kHz
    // ...
    cout << "Construction" << endl;
}

ClassA::~ClassA()
{
    if (GetApStruct != nullptr) {
        if (GetApStruct->fR.refA[0] != nullptr) {
            delete GetApStruct->fR.refA[0];
            GetApStruct->fR.refA[0] = nullptr;
        }
        if (GetApStruct->fR.refA[1] != nullptr) {
            delete GetApStruct->fR.refA[1];
            GetApStruct->fR.refA[1] = nullptr;
        }
        if (GetApStruct->fR.refB[0] != nullptr) {
            delete GetApStruct->fR.refB[0];
            GetApStruct->fR.refB[0] = nullptr;
        }
        if (GetApStruct->fR.refB[1] != nullptr) {
            delete GetApStruct->fR.refB[1];
            GetApStruct->fR.refB[1] = nullptr;
        }
        if (GetApStruct->fR.pVarA != nullptr) {
            delete GetApStruct->fR.pVarA;
            GetApStruct->fR.pVarA = nullptr;
        }
        // do same for struct f1kHz
        // ...
        // finally
        delete GetApStruct;
        GetApStruct = nullptr;
    }
    cout << "Destruction" << endl;
}

int main() {
    {
        ClassA a;
    }

    system("pause");
    return 0;
}

答案 1 :(得分:1)

当你创建一个结构/类对象时,它会保存该对象内存区域中的变量和指针(比如一个对象在内存中占用一些空间。让我们称之为一个框)。使用new()malloc()初始化时,这些指针变量在对象数据所在的框外部给出空间。这些指针现在指向一个位于该对象的内存区域之外的内存区域。现在当对象被破坏时,对象占用的空间(我们称之为框)随着指针变量被破坏。指针指向的存储区仍在程序/进程存储区中。现在我们不知道它的地址是什么或它在哪里。这称为内存泄漏。为避免这种情况,我们需要使用delete关键字取消分配指针引用的内存。我们现在可以自由行了。我尝试用下面的简单图形来说明它。 ObjectA框说明了它在内存中占用的区域。请注意,此容器/框包含本地变量,包括指针。指针指向一些内存位置,比如0xFFF ......并用绿线表示。当我们销毁ObjectA时,它只会破坏其中的所有内容,包括0xFFF地址。但是位于0xFFF的内存仍然在内存中分配。内存泄漏。 ObjectA box illustrates the area occupied by it in the memory. Note that this container/box holds the local varialbes including pointer. The pointer points to some memory location, say 0xFFF... and is illustrated by green line. When we destroy ObjectA, It simply destroys everything in it including 0xFFF address. But the memory located on 0xFFF is still allocated in the memory. A memory leak.

在析构函数中,使用delete关键字显式取消分配内存。哇!我们保存了记忆。

答案 2 :(得分:1)

来自维基百科Resource Acquisition Is Initialization

  

资源获取初始化(RAII)是在C ++中突出使用的编程习惯用语。在RAII中,资源获取在对象创建期间由构造函数完成,而资源释放在对象销毁期间由析构函数完成。如果对象被正确销毁,则不会发生资源泄漏。

因此,您可以在构造函数中新建用于指针的内存,并在析构函数中释放它们:

ClassA::ClassA(void) {
    GetApStruct = new ApStruct;
    GetApStruct->fR.refA[0] = new float{ 1.f };
    GetApStruct->fR.refA[1] = new float{ 2.f };  
}

ClassA::~ClassA(void) {
    delete []GetApStruct->fR.refA;  
    delete GetApStruct;
}

答案 3 :(得分:0)

好吧,让我直接说:

如果您使用的是newdelete,那么您做错了

除非您是有经验的用户,或者您希望实施低级别的项目,否则请不要使用newdelete

相反,使用现有的标准类来处理内存所有权,并在不必要时避免堆分配。作为奖励,您不仅可以避免内存泄漏,还可以避免悬空引用(即删除后使用内存)。

class ClassA : public publicClassA {
public:

private:
    struct ApStruct{
        struct
        {
            float refA[2];
            float refB[2];
            float pVarA;
        } fR;

        struct
        {
            float refA[2];
            float refB[2];
            float pVarA;
        } f1kHz;
    };

    ApStruct GetApStruct;  
}

是的,在你的情况下,它就像删除指针一样简单。否则,如果您想要动态数组(即编译时长度未知的数组),请使用std::vector