Прошло несколько лет с тех пор, как я написал 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 как единственный узел не имеет дочерних узлов.

Затем я перехожу к работе, используя эти данные (вызывая BuildMeshVertices (& i0, & vertices) из main ()), и добавляю потомка в 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 по-прежнему указывает на правильного дочернего элемента, но данные для этого ребенка внезапно искажаются.

Вот скриншот до и после (извините за ссылку, но мне не разрешили использовать Теги IMG)

Я не могу понять, почему это происходит, но у меня такое чувство, что это как-то связано с моим плохим управлением памятью?

ОБНОВЛЕНИЕ . Это не обязательно. Если, например, изменение дочернего вектора на вектор значений является предпочтительным способом C ++, я бы предпочел это. Я пытался прокомментировать ответы, но не уверен, что вы, ребята, видите комментарии (согласно FAQ вам нужно 50 репутации, чтобы оставлять комментарии)?

Ниже приведен полный исходный код (все лишнее удалено, но достаточно, чтобы воспроизвести ошибку):

#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;
}
1
Tchami 5 Авг 2009 в 17:45

5 ответов

Лучший ответ

Измените необработанные указатели на интеллектуальные указатели, и вы получите Поездка из-за проблем с управлением памятью.

Вам не нужно копировать все ускорения в свой проект, только необходимые заголовки.

#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 ++ ... вам следует выбрать язык.

1
fnieto - Fernando Nieto 18 Авг 2009 в 20:22

Вы помещаете указатель на объект стека в свой вектор. Как только выполнение выходит из области видимости, этот объект стека будет уничтожен, а память будет повторно использована, что приведет к фиктивному значению. Пытаться

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

Тогда вам нужно будет освободить эту память в подходящее время.

7
jcopenha 5 Авг 2009 в 13:53

Для работы с указателями следует использовать динамическую память. InputData будет уничтожен при выходе из функции BuildMeshVertices , поэтому данные будут испорчены или вы получите исключение памяти.

Вы должны сделать что-то вроде

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

Вместо того

InputData iDeadEnd = InputData(1.01f, 0.5f);
1
yeyeyerman 5 Авг 2009 в 13:54

Вы создаете экземпляр 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 должна быть явно освобождена, когда вы закончите с ней.

1
Dave Gamble 5 Авг 2009 в 13:54

В момент выхода из функции 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 ) );

Др

Даже это далеко не идеально по понятным причинам. Однако определение дерева элементов никогда не бывает самым увлекательным занятием.

0
Goz 5 Авг 2009 в 14:00