2

C++ での基本的なメモリ管理の原則を理解するのに苦労しています。このコードは、迷路ファイルを 2D ベクトルに読み込む関数の一部であるループの一部です。

Valgrind によると、次のコードはメモリ リークを引き起こしています...

tMazeNodeオブジェクトであり、ノード オブジェクトへのポインタを保持するクラスverts内のベクトルであることに注意してくださいt(オブジェクトと混同しないでくださいMazeNode)。

node* top = new node(TOP, rowCount, i, t.type);
node* bot = new node(BOTTOM, rowCount, i, t.type);
node* left = new node(LEFT, rowCount, i,  t.type);
node* right = new node(RIGHT, rowCount, i, t.type);

t.verts.push_back(top);
t.verts.push_back(bot);
t.verts.push_back(left);
t.verts.push_back(right);

temp.push_back(t);

top = NULL;
bot = NULL;
left = NULL;
right = NULL;
delete top;
delete bot;
delete left;
delete right;

最初は、各ポインターを削除する前に NULL に設定しませんでしたが、割り当てエラーが発生しました。したがって、それらを NULL に設定するだけで、コードが機能します。なぜこれがメモリリークを引き起こすのか、なぜポインターを NULL に設定する必要があるのか​​ 、本当に混乱していると思います。これを行うには、おそらくポインター以外のより簡単な方法がありますが、おそらくこの問題は、メモリ管理をよりよく理解するのに役立ちます。

みんな、ありがとう。

編集: これが MazeNode クラスです (これが「t」です) (また、このクラスを作成する際の怠惰を許し、すべてを構造体のように公開します)

class MazeNode 
{
public:
    void setType(char c);
    char getChar();

    NodeType type;
    vector<Direction> visitedFrom;

    vector<node*> verts;
};

そしてノードクラス:

class node
{
public:
    node();
    node(Direction d, int r, int c, NodeType t);
    ~node(); //empty definition
    node(const node* n);
    node& operator=(const node& n);

    void addAdj(node* a, int w);
    void printAdj() const;
    string direction() const;

    void print() const;
    bool operator<(const node& n) const;


    int distance; //from start
    bool visited;
    node* prev;
    vector<Edge> adj;
    Direction dir;
    int row, col;
    NodeType type;
};

EDIT2:みんなありがとう。私は今問題を理解しています。ポインター オブジェクトのベクトルを変更して、ポインターを使用しないようにしました。

4

6 に答える 6

9

null 代入を追加する前は、メモリ リークとは異なる (さらに悪い) 問題がコードにありました。つまり、メモリ リークとは異なる (より深刻な) 問題がありました。

null 割り当てを追加してメモリ リークを発生させると、パフォーマンスは向上しますが、それほどではありません。

本当の解決策は、ポインターに対して delete を呼び出した後は、ポインターをどこにも保持しないことです。つまり、ここではしないpush_backかしないdeleteでください。

于 2012-04-16T22:00:49.143 に答える
4

ポインターをコンテナーに配置してから、ポインターを削除しています。コードが後でこれらのポインターを使用しようとすると、それらは無効になり、クラッシュが発生します。

ポインターを削除する前にポインターを NULL に設定すると、それらをまったく削除しないことになります。NULL ポインターを削除しても何も起こりません。しかし、後でオブジェクトを削除する必要がなくなり、メモリ リークが発生します。

コード内でポインターを使用していない場所を見つけて、そこを削除する必要があります。

編集:もちろんstd::shared_ptr、オブジェクトを自動的に削除するため、 のようなスマートポインターを使用すると、この面倒が完全に解消されることに言及する必要がありました。

于 2012-04-16T22:04:10.050 に答える
2

エラーは、ポインターのベクトルを使用しています。あなたによると、vertsはこれです:

vector<node*> verts;

しかし、それがどうあるべきかはこれです:

vector<node> verts;

最初のケースでは、ポインターをpush_back()すると問題ありませんが、ベクトルをpop_backまたはその他の方法でサイズ変更すると、ポインターはベクトルの「コンテンツ」であり、割り当てが解除されますが、ポインターのサイズは変更されません。を指します。これはノードです。したがって、ノードがリークします。ただし、2番目のケースでは、ノードはベクトルの「一部」であり、ベクトルのサイズ変更の一部として割り当て/割り当て解除されます。

ここでのパターンは、おそらくJava / C#の背景を示しています。これは、コンテナーへの「新規作成」がこれらの言語で非常に一般的であるためですが、C ++でそれを行うには、スマートポインター(などvector<shared_ptr<node>>)のコンテナーが必要です。おそらく質問の範囲を超えています。しかし、これらの言語では、参照型へのすべての参照は「スマートポインター」(多かれ少なかれ)であるため、これは自動的に行われます。C++はそのようではありません。

を使用するようにコードを変更するvector<node>(そして、コードにプッシュバックする方法を変更する)か、ベクトルが縮小したときにノードの割り当てを明示的に解除する必要があります。

于 2012-04-16T22:06:20.593 に答える
2

この混乱こそが、私がこれらの種類のマクロを作成する理由です。

#define delobj(obj) (delete obj, obj = NULL)

そして、次のように使用します。

delobj(top);
delobj(bot);
delobj(left);
delobj(right);
于 2012-04-16T21:59:59.173 に答える
2

値を削除する前に値を NULL に設定しているため、NULL を削除しようとしていて、何も削除されていません。削除呼び出しを NULL 呼び出しの上に移動してみてください。

于 2012-04-16T21:57:45.740 に答える
0

への変更:

delete top;
delete bot;
delete left;
delete right;

top = NULL;
bot = NULL;
left = NULL;
right = NULL;

そして、それはうまくいくはずです。

于 2012-04-16T21:59:19.977 に答える