【问题标题】:C++ variable data being overwrittenC++ 变量数据被覆盖
【发布时间】:2010-11-17 00:42:29
【问题描述】:

我写 C/C++ 已经有几年了,现在我面临一个我似乎无法靠自己解决的问题。

给定以下结构:

struct InputData
{
    float diameter;
    float length;
    int vertIndex;
    struct InputData *parent;
    vector<InputData*> children;
    bool deadEnd;

    InputData(float dia, float lngth)
    {
        diameter = dia;
        length = lngth;
        vertIndex = NULL;
        parent = NULL;
        deadEnd = false;
    }
};

我首先定义了一些节点,以及它们的父/子关系:

InputData i0 = InputData(3.0f, 3.0f);
InputData i1 = InputData(2.0f, 2.0f);
InputData i2 = InputData(1.0f, 1.0f);
InputData i3 = InputData(1.0f, 1.0f);
InputData i4 = InputData(1.0f, 1.0f);
InputData i5 = InputData(1.01f, 0.5f);

i0.children.push_back(&i1);
i1.children.push_back(&i2);
i2.children.push_back(&i3);
i3.children.push_back(&i4);
i4.children.push_back(&i5);

i1.parent = &i0;
i2.parent = &i1;
i3.parent = &i2;
i4.parent = &i3;
i5.parent = &i4;

注意 i5 作为唯一节点没有任何子节点。

然后我继续使用这些数据做一些工作(从 main() 调用 BuildMeshVertices(&i0, &vertices)),并最终将一个子级添加到 i5:

void BuildMeshVertices(InputData* current, vector<SimpleVertex> *vertices)
{
    //Do work

    if(current->children.size() == 1)
    {
        BuildMeshVertices(current->children[0], vertices);
    }
    else if(current->children.size() == 0 && current->deadEnd == false)
    {
        InputData iDeadEnd = InputData(1.01f, 0.5f);
        iDeadEnd.deadEnd = true;
        iDeadEnd.parent = current;
        current->children.push_back(&iDeadEnd);     

        BuildMeshVertices(&iDeadEnd, vertices);
    }
}

之后一切都很好。 i0 有一个孩子 (i1),i1 有一个孩子 (i2),依此类推,i5 现在也有一个孩子。

我调用了另一个函数(BuildMeshIndices()),突然在这个函数的几行(第 63 行)中,新添加到 i5 的子节点的数据被覆盖了。 i5 仍然指向正确的孩子,但是这个孩子的数据突然乱码。

这是截图before and after(抱歉链接,但我不被允许使用IMG标签)

我不知道为什么会发生这种情况,但我感觉这与我糟糕的内存管理有关?

更新也不必这样做。例如,如果将子向量更改为值向量是首选的 C++ 方式,我更愿意这样做。我试图对答案发表评论,但我不确定你们是否看到了 cmets(根据常见问题解答,您需要 50 声望才能离开 cmets)?

以下是完整的源代码(去掉了所有不必要的内容,但足以重现错误):

#include "stdafx.h"
#include <vector>

using std::vector;

struct InputData
{
    float diameter;
    float length;
    int vertIndex;
    struct InputData *parent;
    vector<InputData*> children;
    bool deadEnd;

    InputData(float dia, float lngth)
    {
        diameter = dia;
        length = lngth;
        vertIndex = NULL;
        parent = NULL;
        deadEnd = false;
    }
};

//--------------------------------------------------------------------------------------
// Vertex types
//--------------------------------------------------------------------------------------
struct SimpleVertex
{
    float Pos;

    SimpleVertex(float Position)
    {
        Pos = Position;
    }
};

void BuildMeshVertices(InputData* current, vector<SimpleVertex> *vertices)
{
    current->vertIndex = vertices->size();

    //Add vertices..

    if(current->children.size() == 1)
    {
        BuildMeshVertices(current->children[0], vertices);
    }
    else if(current->children.size() == 0 && current->deadEnd == false)
    {
        InputData iDeadEnd = InputData(1.01f, 0.5f);
        iDeadEnd.deadEnd = true;
        iDeadEnd.parent = current;
        current->children.push_back(&iDeadEnd);     

        BuildMeshVertices(&iDeadEnd, vertices);
    }
}

void BuildMeshIndices(InputData* current, vector<unsigned long> *indices)
{
    indices->push_back(current->vertIndex+2);
    indices->push_back(current->vertIndex+0);
    indices->push_back(current->vertIndex+1);
    indices->push_back(current->vertIndex+3);
    indices->push_back(current->vertIndex+0);
    indices->push_back(current->vertIndex+2);

    InputData *parent = current->parent;

    unsigned long vOffset;

    if(parent != NULL && parent->children.size() == 1)
    {   
        vOffset = (unsigned long)current->vertIndex;

        indices->push_back(vOffset+7);
        indices->push_back(vOffset+5);
        indices->push_back(vOffset+4);
        indices->push_back(vOffset+6);
        indices->push_back(vOffset+5);
        indices->push_back(vOffset+7);

        indices->push_back(vOffset+10);
        indices->push_back(vOffset+8);
        indices->push_back(vOffset+9);
        indices->push_back(vOffset+11);
        indices->push_back(vOffset+8);
        indices->push_back(vOffset+10);

        indices->push_back(vOffset+15);
        indices->push_back(vOffset+13);
        indices->push_back(vOffset+12);
        indices->push_back(vOffset+14);
        indices->push_back(vOffset+13);
        indices->push_back(vOffset+15);

        indices->push_back(vOffset+18);
        indices->push_back(vOffset+16);
        indices->push_back(vOffset+17);
        indices->push_back(vOffset+19);
        indices->push_back(vOffset+16);
        indices->push_back(vOffset+18);
    }

    if(current->children.size() == 1 && current->deadEnd == false)
    {
        BuildMeshIndices(current->children[0], indices);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    InputData i0 = InputData(3.0f, 3.0f);
    InputData i1 = InputData(2.0f, 2.0f);
    InputData i2 = InputData(1.0f, 1.0f);
    InputData i3 = InputData(1.0f, 1.0f);
    InputData i4 = InputData(1.0f, 1.0f);
    InputData i5 = InputData(1.01f, 0.5f);

    i0.children.push_back(&i1);
    i1.children.push_back(&i2);
    i2.children.push_back(&i3);
    i3.children.push_back(&i4);
    i4.children.push_back(&i5);

    i1.parent = &i0;
    i2.parent = &i1;
    i3.parent = &i2;
    i4.parent = &i3;
    i5.parent = &i4;

    // Create vertex buffer
    vector<SimpleVertex> vertices;

    BuildMeshVertices(&i0, &vertices);

    // Create index buffer
    vector<unsigned long> indices;

    BuildMeshIndices(&i0, &indices);

    return 0;
}

【问题讨论】:

    标签: c++ vector pointers overwrite


    【解决方案1】:

    您正在将指向堆栈对象的指针推送到您的向量中。一旦执行离开范围,堆栈对象将被销毁并且内存将被重用,从而产生虚假值。试试

    InputData *iDeadEnd = new InputData(1.01f, 0.5f);
    iDeadEnd->deadEnd = true;
    iDeadEnd->parent = current;
    current->children.push_back(iDeadEnd);
    

    那么您必须在适当的时候释放该内存。

    【讨论】:

    • 非常感谢您(以及其他提出类似建议的人)!这似乎可以解决问题,但是释放内存的首选方法是什么?什么时候?
    • 你可以使用 boost::shared_ptr 或 boost::ptr_vector
    • 您必须遍历树并手动释放内存。这并不是特别困难,但如果错过了一步,就会出现内存泄漏。
    • 感谢您的回答。虽然这解决了我的问题,但看起来接受的答案是一个更简洁的整体解决方案。很抱歉没有早点澄清我在寻找什么。
    【解决方案2】:

    将原始指针更改为smart pointers,您将解决内存管理问题。

    您不需要将所有 boost 复制到您的项目中,只需要复制所需的标题即可。

    #include <vector>
    #include <boost/shared_ptr.hpp>
    #include <boost/weak_ptr.hpp>
    
    struct InputData
    {
        float diameter;
        float length;
        unsigned long vertIndex;
        boost::weak_ptr<InputData> parent;
        std::vector< boost::shared_ptr<InputData> > children;
        bool deadEnd;
    
        InputData(float dia, float lngth, boost::weak_ptr<InputData> p = boost::weak_ptr<InputData>(), bool de = false)
            : diameter(dia), length(lngth), vertIndex(0), parent(p), deadEnd(de) {}
    };
    
    struct SimpleVertex
    {
        float Pos;
    
        SimpleVertex(float position) : Pos(position) {}
    };
    
    void BuildMeshVertices(boost::shared_ptr<InputData> current, std::vector<SimpleVertex>& vertices)
    {
        current->vertIndex = vertices.size();
    
        //Add vertices..
        if(current->children.size() == 1)
        {
            BuildMeshVertices(current->children[0], vertices);
        }
        else if(current->children.size() == 0 && current->deadEnd == false)
        {
              // this was a stack variable, so the pointer became invalid when going out of ambit.
            boost::shared_ptr<InputData> iDeadEnd( new InputData(1.01f, 0.5f, current, true) );
            current->children.push_back(iDeadEnd);         
    
            BuildMeshVertices(iDeadEnd, vertices);
        }
    }
    
    void BuildMeshIndices(boost::shared_ptr<InputData> current, std::vector<unsigned long>& indices)
    {
        unsigned long vi = current->vertIndex;
        unsigned long  ioffset[] = { vi+2, vi, vi+1, vi+3, vi, vi+2};
        indices.insert(indices.end(), ioffset, ioffset+6);
    
        boost::shared_ptr<InputData> parent = current->parent.lock();
        if (parent && parent->children.size() == 1)
        {   
            unsigned long offs = current->vertIndex;
              unsigned long voffset[] = 
              { offs+7, offs+5, offs+4, offs+6, offs+5, offs+7,
                offs+10, offs+8, offs+9, offs+11, offs+8, offs+10,
                offs+15, offs+13, offs+12, offs+14, offs+13, offs+15,
                offs+18, offs+16, offs+17, offs+19, offs+16, offs+18 };
              indices.insert(indices.end(), voffset, voffset+24);
        }
    
        if(current->children.size() == 1 && current->deadEnd == false)
        {
            BuildMeshIndices(current->children[0], indices);
        }
    }
    
    int main()
    {
        boost::shared_ptr<InputData> i0( new InputData(3.0f, 3.0f) );
        boost::shared_ptr<InputData> i1( new InputData(2.0f, 2.0f) );
        boost::shared_ptr<InputData> i2( new InputData(1.0f, 1.0f) );
        boost::shared_ptr<InputData> i3( new InputData(1.0f, 1.0f) );
        boost::shared_ptr<InputData> i4( new InputData(1.0f, 1.0f) );
        boost::shared_ptr<InputData> i5( new InputData(1.01f, 0.5f) );
    
        i0->children.push_back(i1);
        i1->children.push_back(i2);
        i2->children.push_back(i3);
        i3->children.push_back(i4);
        i4->children.push_back(i5);
    
        i1->parent = i0;
        i2->parent = i1;
        i3->parent = i2;
        i4->parent = i3;
        i5->parent = i4;
    
        // Create vertex buffer
        std::vector<SimpleVertex> vertices;
        BuildMeshVertices(i0, vertices);
    
        // Create index buffer
        std::vector<unsigned long> indices;
        BuildMeshIndices(i0, indices);
    
        return 0;
    }
    

    认为你还有一半 C、一半 C++ 的脏代码……你应该选择一种语言。

    【讨论】:

    • 太棒了!我不知道 boost 库,但看来我必须了解它。
    • 共享/弱指针已添加到 C++03 标准中。一些编译器在 tr1/memory 头文件中的 std::tr1 名称空间中实现了它,或者已经在内存头文件的 std 名称空间中支持 C++0x 的编译器。
    【解决方案3】:

    您应该使用动态内存来处理指针。 InputData会在你退出BuildMeshVertices函数的时候被销毁,所以数据会被垃圾或者你会得到一个内存异常。

    你应该做类似的事情

    InputData * iDeadEnd = new InputData(1.01f, 0.5f);
    

    而不是

    InputData iDeadEnd = InputData(1.01f, 0.5f);
    

    【讨论】:

      【解决方案4】:

      您正在堆栈上实例化 iDeadEnd,并获取指向堆栈地址的指针!当函数终止并且堆栈展开时,iDeadEnd 的数据会出现乱码。

      InputData *iDeadEnd = new InputData(1.01f, 0.5f);
      iDeadEnd->deadEnd = true;
      iDeadEnd->parent = current;
      current->children.push_back(iDeadEnd);         
      
      BuildMeshVertices(iDeadEnd, vertices);
      

      您现在遇到的问题是 iDeadEnd 的内存在您完成后需要显式释放。

      【讨论】:

        【解决方案5】:

        在您的 BuildMeshVertices 函数退出的那一刻,iDeadEnd(i5 的子级)被解构,因为您在堆栈上声明了它,并且通过退出该函数,整个堆栈帧将失效并且所有对象都被解构。您要么想要动态分配 iDeadEnd,要么从根本上重新考虑如何定义树。你最好让每个结构都保存一个 InputData 向量(不是 InputData*),然后按如下方式设置它们:

        InputData i0 = InputData(3.0f, 3.0f);
        i0.children.push_back( InputData( 2.0f, 2.0f ) );
        i0.children[0].children.push_back( InputData( 1.0f, 1.0f ) );
        

        由于显而易见的原因,这远非理想。不过,定义元素树从来都不是最有趣的事情。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-02-25
          • 2013-11-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多