1

構築中のライブラリでいくつかの主要なクラスをテストするために使用する簡単なコンソール プログラムを作成しました。これで、コードはエラーなしで正しくビルドされます。しかし、コードを実行した後、コードの特定のポイントで Index メソッドを呼び出した後、アプリケーションが動作を停止することがわかりました。問題に関する詳細情報を得るために、いくつかの異なる方法でデバッグを試みましたが、収集した情報はまったく役に立ちませんでした。たぶん、私がしていないこと(または間違っていること)を知っている他の誰かを助けるでしょう。

Util 名前空間の内容は次のとおりです。

    template<typename var>
class VectorNode
{
    public:
    VectorNode(var value, VectorNode<var>* next = NULL, VectorNode<var>* prev = NULL)
    {
        data = value;
        t_next = next;
        t_prev = prev;
    }
    ~VectorNode()
    {
        if (t_next != NULL)
            delete t_next;
    }


    virtual VectorNode<var>* Next(){ return t_next; } // get the next node in line
    virtual void Next(VectorNode<var>* newNode){ t_next = newNode; } // set the next node in line

    virtual VectorNode<var>* Prev(){ return t_prev; }// get the previous node in line
    virtual void Prev(VectorNode<var>* newNode){ t_prev = newNode; } // set the previous node in line

    virtual var Value(){ return data; } // get the node's value

    private:
    var data;
    VectorNode<var>* t_next;
    VectorNode<var>* t_prev;
};

template<typename var>
class Vector
{
    public:
    Vector()
    {
        tailNode = new VectorNode<var>(*(new var));
        headNode = new VectorNode<var>(*(new var), tailNode);
        tailNode->Prev(headNode);
        size = new int;
        *size = 0;
    }
    ~Vector()
    {
        delete headNode;
        delete size;
    }


    int Size(){ return *size; } // get the size of a vector
    void Add(var toAdd, int index = 0) // 
    {
        VectorNode<var>* lastNode;
        if (index > (*size))
            index = *size;
        if (index < 1) // add to the end of the vector
        {
            lastNode = tailNode;
        }
        else
        {
            int i;
            if (index <= (*size / 2)) // if the index is less than half the size, iterate forwards
            {
                lastNode = headNode;
                for (i = 1; i <= index; i++){ lastNode = lastNode->Next(); }
            }
            else // otherwise, iterate backwards
            {
                lastNode = tailNode;
                for (i = *size; i >= index; i--){ lastNode = lastNode->Prev(); }
            }
        }
        VectorNode<var>* temp = lastNode->Prev();
        VectorNode<var>* newNode = new VectorNode<var>(toAdd, lastNode, temp);
        lastNode->Prev(newNode);
        temp->Next(newNode);
        *size = *size + 1;
    }
    void Remove(int index) // remove an index
    {
        VectorNode<var>* toRemove;
        VectorNode<var>* lastNode;
        int i;
        if ((index > *size) || (index < 1)) // if not in the domain...
            index = *size;
        if (index <= (*size / 2)) // iterate forwards
        {
            lastNode = headNode;
            for (i = 1; i < index+2; i++){ lastNode = lastNode->Next(); }
        }
        else // iterate backwards
        {
            lastNode = tailNode;
            for (i = *size; i > index; i--){ lastNode = lastNode->Prev(); }
        }
        toRemove = lastNode->Prev();
        VectorNode<var>* temp = toRemove->Prev();
        temp->Next(lastNode);
        lastNode->Prev(temp);
        delete toRemove;
        *size = *size - 1;
    }
    var Index(int index) // get the value of a node
    {
        VectorNode<var>* lastNode;
        int i;
        if (index <= (*size / 2)) // iterate forwards
        {
            lastNode = headNode;
            for (i = 1; i <= index; i++){ lastNode = lastNode->Next(); }
        }
        else // iterate backwards
        {
            lastNode = tailNode;
            for (i = *size; i >= index; i--){ lastNode = lastNode->Prev();}
        }
        return lastNode->Value();
    }

    private:
    int* size;
    VectorNode<var>* tailNode; // the head and tail nodes are placeholders, to keep the list inside its boundaries
    VectorNode<var>* headNode;
};

それを読みたくない場合は、各メソッドにコメントを付けて、その全体的な目的を説明します。また、いくつかのコード ブロックの簡単な説明を追加してみました。

そして、これがエントリ関数と包含です。

#include "iostream"

#include "stdlib.h" // this has nothing in it that's being used
#include "testhead.h" // the location of the Util namespace

int main() { using namespace Util;

Vector<int>* x = new Vector<int>();
x->Add(42);
x->Add(24);
x->Add(12);
x->Add(21);
std::cout << "Listing Indices\n";
for (int i = 1; i <= x->Size(); i++)
{
    std::cout << i << "\t" << x->Index(i) << "\n";
}
std::cout << "Size(pre-removal):\t" << x->Size() << "\n";
x->Remove(2);
std::cout << "Size(post-removal):\t" << x->Size() << "\n";
std::cout << "Listing Indices\n";
std::cout << 3 << "\t" << x->Index(3) << "\n";
for (int i = 1; i <= x->Size(); i++)
{
    std::cout << i << "\t" << x->Index(i) << "\n";
}
system("Pause");

}

さて、私が得た結果はこれです。Remove メソッドを使用する前は、Vector クラスから任意のインデックスに自由にアクセスできます。ただし、remove メソッドを使用した後は、どのインデックスを削除しても、それより上のインデックスにはアクセスできません。ただし、最初のインデックスを削除した場合は、インデックスにアクセスできません。コードをステップ実行しようとしましたが、index メソッドのこのコード行にたどり着きました。

else
{
lastNode = tailNode;
for (i = *size; i >= index; i--){ lastNode = lastNode->Prev();} // error occurs after running this line
}

ここで、Remove メソッドが問題の原因であることがわかったので、戻ってそれに関する出力を得ました。実行を終了する前に、次の行を 2 回実行しました。toRemove が削除される前に 1 回、削除された後にもう一度。

std::cout << (lastNode->Prev() == temp) << "\t" << (temp->Next() == lastNode) << "\n";

削除される前に、比較が真であることを示す 1 が 2 回出力されます。しかし、Prev メソッドと Next メソッドのいずれかを 2 回目に呼び出すと、プログラムがフリーズします。これは、メモリ内の場所を解放したためであることはわかっていますが、比較すると、削除したノードへの他のノードからの参照がなくなっていることがわかります。さて、私の具体的な質問は、なぜこれが発生しているのか、どうすれば修正できるのかということです。私はヒープ上のメモリの管理について少し知っていますが、これがプログラムに問題を引き起こすようには見えません。ですから、誰かが親切にそれを提供してくれるなら、なぜこれが起こるのかについて簡単な説明を使用できます.

少しでも参考になれば、私は Code::Blocks IDE と GNU GCC コンパイラーを使用しています。また、質問の仕方に関連して何か間違ったことをしている場合は教えてください。私は Stack Overflow に頻繁にアクセスすることはありませんし、ここで質問することもありません。これは、私が知っているあなたの質問に答えるのに最適な場所です.

4

3 に答える 3

2

VectorNode クラスのデストラクタは、t_next ポインタによってオブジェクト ポインタを削除します。toRemove ポインターで delete を呼び出すということは、その VectorNode オブジェクトのデストラクタが呼び出され、次に次のデストラクタが呼び出され、次に次のデストラクタが呼び出されることを意味します。

基本的に、toRemove を削除すると、toRemove とそれ以降のすべてのオブジェクトが削除されます。これにより、tailNode の t_prev が既に解放されているメモリを指すようになり、Index 関数でそれらのポインタを逆参照しようとしますが、これは良いことではありません。

于 2010-10-29T23:26:53.507 に答える
1

remove を呼び出すとノードが削除されますが、ノードを削除するとすべてのノードが削除されます。

~VectorNode()
{
    if (t_next != NULL)
        delete t_next;
}

したがって、1 ベースのベクトルの要素 2 を削除すると、経験したように他のすべての要素が削除され、調査する要素 3 がなくなります。

于 2010-10-29T23:38:45.893 に答える
1

メンバーが他のノードを指している を削除すると、 のデストラクタがVectorNodeその他のノードを削除します (さらにノードを削除する可能性があります)。t_nextVectorNode

を使用してリストの途中からノードを削除するとRemove()t_nextこのノードの はリストのさらに別のノードを指します。このノードが削除されると、デストラクタはリスト内でそれ以降のすべてのノードも削除します。この半分削除されたリストを使い続けると、あらゆる種類の問題が発生します。

その他のランダムな観測:

  • が通常のorsizeint*代わりにあるのはなぜですか? これがポインタであるべき理由がわかりません。intsize_t
  • new VectorNode<var>(*(new var))new VectorNode<var>(var())不必要にメモリをリークしないようにする必要があります。
  • 事前のt_next != NULLテストdeleteは不要です
  • から派生したクラスを作成する予定はありますVectorNode<>か? そうでない場合、メソッドが必要になる理由はありませんvirtual
  • で 1 から始まるインデックスを使用するのAdd()は珍しいことです。0 から始まるインデックスを期待するでしょう。

また、この種の構造を実装するstd::list<>やのような標準ライブラリ コンテナがあることもお伝えしなければなりません。std::vector<>

于 2010-10-29T23:28:33.450 に答える