Вопрос

Прошло несколько лет с тех пор, как я написал 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;
}
Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

Вам следует сделать что-то вроде

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

вместо

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

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

В тот момент, когда ваша функция 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 ) );

и т. д.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top