【问题标题】:c++ correct deletion of structs and pointersc ++正确删除结构和指针
【发布时间】:2015-10-22 07:48:15
【问题描述】:

我有一个关于如何正确删除结构及其内部声明的相应指针的问题。

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

下面的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
}

希望这一切都有意义,

编辑: 我在代码中更新了一个错误的行 - 但是问题仍然是一样的。在我实施解决方案之前,我会先看看下面的内容。

2015 年 10 月 24 日编辑 我一直在尝试以下一些建议,但我无法找到解决问题的方法,我必须承认我也很难缩小可能导致问题的范围。

我的代码是 DLL 的一部分。该代码包含了一些我无法控制的源代码,因此我有有限的选择如何使用构造函数和指针上的 new 进行初始化。

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

在以下情况下我会得到不同的结果:

  1. 调用 InitCode - 一次!
  2. 然后我将多次调用 CallCode - 进行计算
  3. 销毁类的实例

当我再次重复上述操作时,我得到的结果与我第一次运行代码时不同,但之后它保持不变。

如果我加入魔法线似乎一切正常???

更新的 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;
        ...
    }
}

感谢大家的支持,

托马斯

【问题讨论】:

  • 您确定需要指针数组吗? std::array<float, 2> 呢?
  • values ... are kept in the memory next time i initiate a class 这不是错误,可能会根据情况发生(但您还有其他错误)。如果您想删除某些内容,请在删除之前用 0 覆盖它。
  • 我需要以这种方式声明的数组才能包装原始代码。所以我不能真正改变数组的语法是我“不能”改变使用它的源代码。解释起来有点复杂。
  • @deviantfan 不能删除吗?所以下次我启动课程时,它会重新分配所需的空间吗?我的 vars 比上面显示的要多,其中一些没有初始化为 0。
  • 我认为你需要编写函数来重置你的值。原因 C++ 不会像在 Java 或 C# 中那样初始化值。

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


【解决方案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;
}

【讨论】:

    【解决方案2】:

    当您创建一个结构/类对象时,它会在该对象的内存区域中保存变量和指针(假设一个对象占用了内存中的一些空间。我们称它为一个盒子)。当使用new()malloc() 初始化时,这些指针变量在对象数据所在的框之外被赋予空间。这些指针现在指向该对象内存区域之外的某个内存区域。现在,当对象被破坏时,对象所占用的空间(我们称之为盒子)随着指针变量而被破坏。指针指向的内存区域仍在程序/进程内存区域中。现在我们不知道它的地址是什么或它在哪里。这就是所谓的内存泄漏。为了避免这种情况,我们需要使用delete关键字来释放指针引用的内存。我们现在可以走了。我试图用下面的简单图形来说明它。 ObjectA 框说明了它在内存中占用的区域。请注意,此容器/框包含本地变量,包括指针。指针指向某个内存位置,例如 0xFFF...,并用绿线表示。当我们销毁 ObjectA 时,它只会销毁其中的所有内容,包括 0xFFF 地址。但是位于 0xFFF 的内存仍然分配在内存中。内存泄漏。

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

    【讨论】:

      【解决方案3】:

      来自维基百科Resource Acquisition Is Initialization

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

      【讨论】:

      • 您使用的是delete[],但不是new[]。那是未定义的行为。
      【解决方案4】:

      好吧,让我直接说:

      如果您使用的是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

      【讨论】:

        猜你喜欢
        • 2013-12-06
        • 1970-01-01
        • 1970-01-01
        • 2017-05-30
        • 1970-01-01
        • 2012-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多