Domanda

Sono passati alcuni anni da quando ho scritto C / C ++ e ora sto affrontando un problema che proprio non riesco a risolvere da solo.

Data la seguente struttura:

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

Comincio definendo un numero di nodi e la loro relazione padre / figlio:

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;

Nota che i5 come unico nodo non ha figli.

Passo poi a fare un po 'di lavoro usando questi dati (chiamando BuildMeshVertices (& amp; i0, & amp; vertices) da main ()), e finisco per aggiungere un figlio a 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);
    }
}

Dopo di che tutto va bene. i0 ha un figlio (i1), i1 ha un figlio (i2), così via e così via e i5 ora ha anche un figlio.

Chiamo un'altra funzione (BuildMeshIndices ()) e improvvisamente alcune righe in questa funzione (riga 63) i dati per il bambino appena aggiunto a i5 vengono sovrascritti. i5 indica ancora il bambino giusto, ma i dati per questo bambino vengono improvvisamente confusi.

Ecco uno screenshot prima e dopo (mi dispiace per il link, ma non lo ero autorizzato a utilizzare i tag IMG)

Non riesco a capire perché questo accada, ma ho la sensazione che abbia qualcosa a che fare con la mia scarsa gestione della memoria?

AGGIORNAMENTO Inoltre non deve essere fatto in questo modo. Se per esempio cambiare il vettore figlio in un vettore di valori è il modo C ++ preferito, lo preferirei di gran lunga. Ho provato a commentare le risposte, ma non sono sicuro che voi ragazzi stiate vedendo i commenti (secondo le FAQ avete bisogno di 50 reputazione per lasciare commenti)?

Di seguito è riportato il codice sorgente completo (con tutto ciò che non è necessario eliminato, ma abbastanza per riprodurre l'errore):

#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;
}
È stato utile?

Soluzione

Cambia i puntatori non elaborati in puntatori intelligenti e avrai problemi di gestione della memoria.

Non è necessario copiare tutti i boost nel progetto, solo le intestazioni richieste.

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

Pensi di avere ancora mezzo codice sporco C e mezzo C ++ ... dovresti scegliere una lingua.

Altri suggerimenti

Stai spingendo il puntatore su un oggetto stack nel tuo vettore. Una volta che l'esecuzione lascia l'ambito, l'oggetto stack verrà distrutto e la memoria verrà riutilizzata, risultando in un valore fasullo. Prova

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

Quindi dovrai liberare quella memoria al momento opportuno.

Dovresti usare la memoria dinamica per lavorare con i puntatori. InputData verrà distrutto quando si esce dalla funzione BuildMeshVertices , quindi i dati verranno confusi o si otterrà un'eccezione di memoria.

Dovresti fare qualcosa del genere

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

anziché

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

Installi iDeadEnd sullo STACK e stai prendendo un puntatore all'indirizzo dello stack! Quando le funzioni terminano e lo stack si svolge, i dati per iDeadEnd andranno in rovina.

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

BuildMeshVertices(iDeadEnd, vertices);

Il problema che hai ora è che la memoria per iDeadEnd deve essere esplicitamente allocata quando hai finito.

Nel momento in cui la tua funzione BuildMeshVertices viene chiusa, iDeadEnd (figlio di i5) viene decostruito perché lo hai dichiarato nello stack e uscendo dalla funzione l'intero frame dello stack viene invalidato e tutti gli oggetti decostruiti. O vuoi allocare dinamicamente iDeadEnd o ripensare radicalmente come definisci un albero. Faresti meglio ad avere ogni struttura che contiene un vettore di InputData (non InputData *) e quindi impostarli come segue:

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

etc

Anche questo è tutt'altro che ideale per ovvie ragioni. Definire un albero di elementi non è mai la cosa più divertente da fare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top