2017-07-29 21 views
0

アルゴリズムの問​​題を解決するためのリンクリストを実装しようとしていました。
それは基本的には機能しましたが、私はあまりにも多くのメモリを使用していました。
次のようなデストラクタデザインの欠陥を指摘していただければ幸いです。リンクリストの破棄

template<typename T> 
struct Node { 
    Node(): item(0),next(0) {} 
    Node(T x): item(x),next(0) {} 
    T item; 
    Node* next; 
}; 

template <typename T> 
struct List { 
    List() : head(0),tail(0) {} 
    Node<T>* head; 
    Node<T>* tail; 
    void insert(T x) { 
     Node<T>* newNode = new Node<T>(x); 
     if(head == NULL) { 
      head = tail = newNode; 
     } else { 
      tail->next = newNode; 
      tail = tail->next; 
     } 
    } 

    void clearRecur(Node<T>* h) { 
     if(h) { 
      clearRecur(h->next); 
      delete h; 
     } 
    } 

    void clear() { 
     if(head) { 
      clearRecur(head); 
     } 
    } 
}; 
+1

は「あまりにも」どのくらいですか? – user463035818

+0

あなたはメモリが漏れているように見えますが、与えられたコードのスニペットからは分かりにくいです。 – user0042

+1

初心者の方にはここでの参照がありません。リストを 'clearRecur'で解放しますが、' tail'も 'h 'の前の要素も変更しないでください。同じことが 'clear'にも適用されます。ここで' head'と 'tail'はまだ設定されていますが、すでに解放されています。 – amit

答えて

0

リストは反復的または反復的にクリアすることができます。

あなたの(IMHOの正しい)バージョンに代わって、私は若干異なるアプローチを使用します。Node自身を "責任を負って"その尾を削除するようにします。これにより、再帰的な消去も行われます(コードは少なくなります)。

再帰クリア:

template<typename T> 
struct Node { 
    Node(): item(), next(nullptr) {} 
    Node(T x): item(x), next(nullptr) {} 

    ~Node() { delete next; } // <== recursive clearing 

    T item; 
    Node* next; 

    // Using the default copy ctor would corrupt the memory management. 
    // Deleting it lets the compiler check for accidental usage. 
    Node(const Node&) = delete; 
    // Deleting assignment operator as well. 
    Node& operator=(const Node&) = delete; 
}; 

template <typename T> 
struct List { 
    List() : head(nullptr), tail(nullptr) {} 
    ~List() { clear(); } 
    Node<T>* head, tail; 
    void insert(T x) { 
     Node<T>* newNode = new Node<T>(x); 
     if (head == nullptr) head = tail = newNode; 
     else { 
      tail->next = newNode; 
      tail = tail->next; 
     } 
    } 

    void clear() { 
     delete head; 
     head = tail = nullptr; 
    } 

    // Using the default copy ctor would corrupt the memory management. 
    // Deleting it lets the compiler check for accidental usage. 
    List(const List&) = delete; 
    // Delete assignment operator as well. 
    List& operator=(const List&) = delete; 
}; 

これは、私たちの現在のプロジェクトでそれをやった方法です。一見すると、シンプルで機能しているように見えました。私たちのベータテスターが活躍したとき、私は気が変わった。実際のプロジェクトでは、リストの長さが長くなり、再帰的なクリアにスタックメモリが不足していました。 (Yepp – スタックオーバーフロー)私はよく知っていたはずです!

したがって、私は「責任」がNodeからListに戻されるように反復して–をクリアしました。 (API、それは「ボンネットの下に」起こるように、ユーザーがこれを注意しません。)

反復クリア:

template<typename T> 
struct Node { 
    Node(): item(), next(nullptr) {} 
    Node(T x): item(x), next(nullptr) {} 
    T item; 
    Node* next; 
}; 

template <typename T> 
struct List { 
    List() : head(nullptr), tail(nullptr) {} 
    ~List() { clear(); } 
    Node<T>* head, tail; 
    void insert(T x) { 
     Node<T>* newNode = new Node<T>(x); 
     if (head == nullptr) head = tail = newNode; 
     else { 
      tail->next = newNode; 
      tail = tail->next; 
     } 
    } 

    void clear() { 
     while (head) { 
      Node<T> *p = head; head = head->next; 
      delete p; 
     } 
     tail = nullptr; 
    } 

    // Using the default copy ctor would corrupt the memory management. 
    // Deleting it lets the compiler check for accidental usage. 
    List(const List&) = delete; 
    // Delete assignment operator as well. 
    List& operator=(const List&) = delete; 
}; 
+0

私たちはFP(関数型プログラミング)の土地ではないので、リスト破壊のための再帰(特に)を避けることを推奨します。 –

関連する問題