上書きされるC ++変数データ
質問
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(&amp; i0、&amp; 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には1つの子(i1)があり、i1には1つの子(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 は破棄されるため、データがガベージされるか、メモリ例外が発生します。
次のようにする必要があります
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 ) );
etc
それでも明らかな理由で理想からはほど遠い。ただし、要素のツリーを定義することは、やるべきことの中で最も楽しいことではありません。