web-dev-qa-db-ja.com

C ++テンプレート-LinkedList

編集-以下に回答しましたが、中括弧を見逃しました。皆さんありがとう。

私は、他のプログラムで使用できる基本的な単一リンクリストを作成しようとしています。組み込み型とユーザー定義型で動作できるようにしたいと思います。つまり、テンプレート化する必要があります。

このため、格納する情報がわからないため、ノードもテンプレート化する必要があります。私は次のようにノードクラスを書きました-

template <class T> class Node
{
    T data; //the object information
    Node* next; //pointer to the next node element

public:
    //Methods omitted for brevity
};

リンクリストクラスは別のクラスに実装されており、リストの最後に新しいノードを追加するときにノードをインスタンス化する必要があります。私はこれを次のように実装しました-

#include <iostream>
#include "Node.h"
using namespace std;

template <class T> class CustomLinkedList
{
    Node<T> *head, *tail;

public:

    CustomLinkedList()
    {
        head = NULL;
        tail = NULL;
    }

    ~CustomLinkedList()
    {

    }

    //Method adds info to the end of the list
    void add(T info)
    {
        if(head == NULL) //if our list is currently empty
        {
            head = new Node<T>; //Create new node of type T
            head->setData(info);
            tail = head;
        }
        else //if not empty add to the end and move the tail
        {
            Node* temp = new Node<T>;
            temp->setData(info);
            temp->setNextNull();
            tail->setNext(temp);
            tail = tail->getNext();
        }
    }

    //print method omitted
};

私は次のようにドライバー/テストクラスを設定しました-

#include "CustomLinkedList.h"
using namespace std;

int main()
{
    CustomLinkedList<int> firstList;

    firstList.add(32);
    firstList.printlist();
    //Pause the program until input is received
    int i;
    cin >> i;

    return 0;
}

ただし、コンパイル時にエラーが発生します-エラーC2955: 'ノード':クラステンプレートの使用にはテンプレート引数リストが必要です-これは、addメソッドの次のコード行を示しています-

Node* temp = new Node<T>;

ドライバークラスで作成されたときにリンクリストに渡されたため、これにタイプに関する情報がない理由がわかりません。 タイプ情報をノードに渡すにはどうすればよいですか?

個別のクラスの代わりにプライベートノード構造体を作成し、両方のクラスのメソッドを1つのファイルに結合する必要がありますか?これで問題が解決するかどうかはわかりませんが、解決できると思います。可能であれば、別のクラスを用意したいと思います。

ありがとう、アンドリュー。

11
Drew_StackID

試してみたいかもしれません

Node<T>* temp = new Node<T>;

また、リストの設計方法のヒントを得るために、もちろんstd :: listを見ることができますが、少し気が遠くなることもあります。

9
villintehaspam

答えはすでに出ていますが、私は塩の粒を追加すると思います。

テンプレートクラスを設計するときは、(ある日)特定の詳細を変更したい場合に備えて、テンプレート引数をほぼすべての場所で繰り返さないことをお勧めします。一般に、これはtypedefを使用して行われます。

template <class T>
class Node
{
public:
  // bunch of types
  typedef T value_type;
  typedef T& reference_type;
  typedef T const& const_reference_type;
  typedef T* pointer_type;
  typedef T const* const_pointer_type;

  // From now on, T should never appear
private:
  value_type m_value;
  Node* m_next;
};


template <class T>
class List
{
  // private, no need to expose implementation
  typedef Node<T> node_type;

  // From now on, T should never appear
  typedef node_type* node_pointer;

public:
  typedef typename node_type::value_type value_type;
  typedef typename node_type::reference_type reference_type;
  typedef typename node_type::const_reference_type const_reference_type;
  // ...

  void add(value_type info);

private:
  node_pointer m_head, m_tail;
};

また、クラス宣言の外部でメソッドを定義することをお勧めします。これにより、インターフェイスが読みやすくなります。

template <class T>
void List<T>::add(value_type info)
{
  if(head == NULL) //if our list is currently empty
  {
    head = new node_type;
    head->setData(info);
    tail = head;
  }
  else //if not empty add to the end and move the tail
  {
    Node* temp = new node_type;
    temp->setData(info);
    temp->setNextNull();
    tail->setNext(temp);
    tail = tail->getNext();
  }
}

さて、いくつかの意見:

  • insertメソッドがSTLで行うように、List<T>::addが新しく追加されたオブジェクトにイテレータを返す場合はよりユーザーフレンドリーになります(名前を変更して挿入することもできます)
  • List<T>::addの実装では、メモリをtempに割り当ててから、一連の操作を実行します。スローが発生した場合は、メモリリークが発生しています。
  • setNextNull呼び出しは必要ありません。Nodeのコンストラクターは、すべてのデータメンバーをm_nextを含む意味のある値に初期化する必要があります。

だからここに改訂版があります:

template <class T>
Node<T>::Node(value_type info): m_value(info), m_next(NULL) {}

template <class T>
typename List<T>::iterator insert(value_type info)
{
  if (m_head == NULL)
  {
    m_head = new node_type(info);
    m_tail = m_head;
    return iterator(m_tail);
  }
  else
  {
    m_tail.setNext(new node_type(info));
    node_pointer temp = m_tail;
    m_tail = temp.getNext();
    return iterator(temp);
  }
}

適切なコンストラクターを使用するという単純な事実により、例外の安全性がどのように向上するかに注意してください。コンストラクター中に何かがスローされた場合、メモリを割り当てないためにnewが必要になるため、何もリークされず、まだ操作を実行していません。 List<T>::insertメソッドは復元力があります。

最後の質問:

単一のリンクリストの通常のinsertメソッドは、簡単なため、最初に挿入されます。

template <class T>
typename List<T>::iterator insert(value_type info)
{
  m_head = new node_type(info, m_head); // if this throws, m_head is left unmodified
  return iterator(m_head);
}

最後にインサートを付けてよろしいですか?または、従来のベクトルとリストのPush_backメソッドのために、このようにしましたか?

12
Matthieu M.

その行は読む必要があります

Node<T>* temp = new Node<T>;

Nodeクラスのnextポインタについても同じです。

1
sepp2k

述べたように、解決策は

Node<T>* temp = new Node<T>;

... Node自体は型ではないため、Node<T>です。

1

この方法で新しいノードを追加する必要があります

Node<T>* temp=new node<T>;

あなたが解決したことを願っています:)

0

必要なもの:

Node<T> *temp = new Node<T>;

この問題が再び発生するのを防ぐために、CustomLinkedListクラスのtypedef NodeType = Node<T>の価値があるかもしれません。

0
P-Nuts

また、printlistでもNode * tempのテンプレートパラメータを指定する必要があります。

0
UnknownGuy
#include<iostream>
using namespace std;

template < class data > class node {
    private :
        data t;
        node<data > *ptr;
    public:
    node() {
        ptr = NULL;
    }
    data get_data() {
        return t;
    }
    void set_data(data d) {
        t = d;
    }
    void set_ptr(node<data > *p) {
        ptr = p;
    }
    node * get_ptr() {
        return ptr;
    }
};
template <class data > node < data > * add_at_last(data  d  , node<data > *start) {
    node< data > *temp , *p  = start;
    temp = new node<data>();
    temp->set_data(d);
    temp->set_ptr(NULL);
    if(!start) {
        start = temp;
        return temp;
    }
    else {
        while(p->get_ptr()) {
            p = p->get_ptr();
        }
        p->set_ptr(temp);
    }
}
template < class data > void display(node< data > *start) {
    node< data > *temp;
    temp = start;
    while(temp != NULL) {
        cout<<temp->get_data()<<" ";
        temp = temp->get_ptr();
    }
    cout<<endl;
}
template <class data > node < data > * reverse_list(node<data > * start) {
    node< data > *p = start , *q = NULL , *r = NULL;
    while(p->get_ptr()) {
        q = p;
        p = p->get_ptr();
        q->set_ptr(r);
        r = q;
    }
    p->set_ptr(r);
    return p;
}
int main() {
    node < int > *start;
    for(int i =0 ; i < 10 ; i ++) {
        if(!i) {
            start = add_at_last(i , start);
        }
        else {
            add_at_last(i , start);
        }
    }
    display(start);
    start = reverse_list(start);
    cout<<endl<<"reverse list is"<<endl<<endl;
    display(start);
}
0
// file: main.cc

#include "linkedlist.h"

int main(int argc, char *argv[]) {
    LinkedList<int> list;
    for(int i = 1; i < 10; i++) list.add(i);
    list.print();
}

// file: node.h

#ifndef _NODE_H
#define _NODE_H

template<typename T> class LinkedList;
template<typename T>class Node {
    friend class LinkedList<T>;
    public:
        Node(T data = 0, Node<T> *next = 0)
            : data(data), next(next)
        { /* vacio */ }
    private:
        T data;
        Node<T> *next;
};

#endif//_NODE_H

// file: linkedlist.h

#ifndef _LINKEDLIST_H
#define _LINKEDLIST_H

#include <iostream>
using namespace std;

#include "node.h"

template<typename T> class LinkedList {
    public:
        LinkedList();
        ~LinkedList();
        void add(T);
        void print();
    private:
        Node<T> *head;
        Node<T> *tail;
};

#endif//_LINKEDLIST_H

template<typename T>LinkedList<T>::LinkedList()
    : head(0), tail(0)
{ /* empty */ }

template<typename T>LinkedList<T>::~LinkedList() {
    if(head) {
        Node<T> *p = head;
        Node<T> *q = 0;

        while(p) {
            q = p;
            p = p->next;
            delete q;
        }

        cout << endl;
    }
}

template<typename T>LinkedList<T>::void add(T info) {
    if(head) {
        tail->next = new Node<T>(info);
        tail = tail->next;
    } else {
        head = tail = new Node<T>(info);
    }
}

template<typename T>LinkedList<T>::void print() {
    if(head) {
        Node<T> *p = head;

        while(p) {
            cout << p->data << "-> ";
            p = p->next;
        }

        cout << endl;
    }
}
0
thecharliex