線形リストを作ってみる その3
その2の続き。。。
線形リストのようなデータ構造は、int型以外にもfloat,double,boolなどいろいろな型を扱うことができると便利です。いろいろな数値型を扱えるように改造してみたいと思います。
Nodeクラスのテンプレートクラス化
Nodeに好きな型を入れられるようにするため、テンプレートクラスにしてみます。
テンプレートクラスは、実装部分をヘッダに記述しなければいけないので、hppファイルとして一つにまとめてみます。
Node.hpp
ヘッダ部分です。template
#ifndef __Node_H #define __Node_H #define TEMPLATE template<class Type> TEMPLATE class Node { public: Node(); ~Node(); Node*& next(); //次のノードのアドレスを取得する Node*& prev(); //前のノードのアドレスを取得する Type& data(); //ノードが保持するデータを取得する void print();//メンバ変数を出力する(テスト用) private: Node* m_next; //次のノードのアドレス Node* m_prev; //前のノードのアドレス Type m_data; //ノードが保持するデータ };
メンバ関数のテンプレート関数化
実装部分もNode.hppに記述します。テンプレート化のコードを追加して、intをtypeへ変更します。変更点は、各メンバ関数をテンプレート化するので、template
Node.hppの続き
TEMPLATE Node<Type>::Node(void): m_next(0), m_prev(0), m_data(0) { } TEMPLATE Node<Type>::~Node(void) { } TEMPLATE Node<Type>*& Node<Type>::next(){ return m_next; } TEMPLATE Node<Type>*& Node<Type>::prev(){ return m_prev; } TEMPLATE Type& Node<Type>::data(){ return m_data; } #include <iostream> using namespace std; TEMPLATE void Node<Type>::print(){ cout <<"prev["<<m_prev<<"]\t"; cout <<"["<<this<<",data="<<m_data<<"]\t"; cout <<"next["<<m_next<<"]"; } #endif
Listクラスのテンプレート化
Nodeクラスと同様にListクラスもテンプレートクラスに書き換えることにします。template
List.hpp
#ifndef __List_H #define __List_H #include "Node.hpp" #define TEMPLATE template<class Type> TEMPLATE class List { public: List(void); ~List(void); void push_back(const Type& data); //要素を最後尾に追加する void pop_back(); //最後の要素を削除する void clear(); //全てのノードを削除する Type& operator[](unsigned int i); //i番目の要素を取り出す void print(); //要素を表示する private: void insert_next(Node<Type>* node,const Type& data); //指定したnodeの後に追加 void insert_prev(Node<Type>* node,const Type& data); //指定したnodeの前に追加 void erase(Node<Type>* node); //指定したノードを削除する Node<Type>* node(unsigned int i); //i番目のノードを取得する Node<Type>* m_root; int m_size; };
テンプレートListクラスの実装部分
List.hppの続き
TEMPLATE List<Type>::List(void): m_size(0) { m_root = new Node<Type>(); m_root->next() = m_root; //rootノードの前後を自分自身にしておく m_root->prev() = m_root; } TEMPLATE List<Type>::~List(void) { clear(); delete m_root; } TEMPLATE Type& List<Type>::operator[](unsigned int i){ return node(i)->data(); } TEMPLATE void List<Type>::push_back(const Type& data){ insert_prev(m_root,data); } TEMPLATE void List<Type>::pop_back(){ erase(m_root->prev()); } TEMPLATE void List<Type>::clear(){ for(Node<Type>* node=m_root->next();node!=m_root;node=m_root->next()){ erase(node); } m_size=0; } TEMPLATE void List<Type>::insert_next(Node<Type>* node,const Type& data){ if(node==0){ return; } Node<Type>* new_node=new Node<Type>();//新たに追加したnode new_node->prev()=node; new_node->next()=node->next(); node->next()->prev()=new_node; node->next()=new_node; new_node->data()=data; //追加したnodeの値を設定 m_size++; } TEMPLATE void List<Type>::insert_prev(Node<Type>* node,const Type& data){ if(node==0){ return; } Node<Type>* new_node=new Node<Type>();//新たに追加したnode new_node->prev()=node->prev(); new_node->next()=node; node->prev()->next()=new_node; node->prev()=new_node; new_node->data()=data;//追加したnodeの値を設定 m_size++; } TEMPLATE void List<Type>::erase(Node<Type>* node){ if(node==0 || node==m_root){ //rootは削除しない return; } node->prev()->next()=node->next(); node->next()->prev()=node->prev(); delete node; m_size--; } TEMPLATE Node<Type>* List<Type>::node(unsigned int i){ int count=0; for(Node<Type>* node=m_root->next();node!=m_root;node=node->next()){ if(count==i){ return node; } count++; } return m_root;//out of range } #include <iostream> using namespace std; //要素を表示する TEMPLATE void List<Type>::print(){ cout <<"(root) "<<"\t\t"; m_root->print(); cout<<endl; int count=0; for(Node<Type>* node=m_root->next();node!=m_root;node=node->next()){ cout <<"(node"<<count<<") "<<"\t"; node->print(); cout<<endl; count++; } } #endif
リストクラスの利用
float型でテストした例は下のようになります。
TestList.cpp
#include "List.hpp" #include <iostream> using namespace std; int main(){ List<float> list; const float x=11.0f; list.print(); cout << endl; list.push_back(x); list.print(); cout << endl; list.push_back(2.2f); list.print(); cout << endl; list.push_back(3.2f); list.print(); cout << endl; list.pop_back(); list.print(); cout << endl; cout<<list[1]<<endl;; list.clear(); list.print(); cout << endl; list.pop_back(); list.print(); cout << endl; cout<<(list[2]=1.09f)<<endl;; return 0; }
(root) prev[00888FC8] [00888FC8,data=0] next[00888FC8]
(root) prev[00882328] [00888FC8,data=0] next[00882328]
(node0) prev[00888FC8] [00882328,data=11] next[00888FC8]
(root) prev[00882360] [00888FC8,data=0] next[00882328]
(node0) prev[00888FC8] [00882328,data=11] next[00882360]
(node1) prev[00882328] [00882360,data=2.2] next[00888FC8]
(root) prev[00882398] [00888FC8,data=0] next[00882328]
(node0) prev[00888FC8] [00882328,data=11] next[00882360]
(node1) prev[00882328] [00882360,data=2.2] next[00882398]
(node2) prev[00882360] [00882398,data=3.2] next[00888FC8]
(root) prev[00882360] [00888FC8,data=0] next[00882328]
(node0) prev[00888FC8] [00882328,data=11] next[00882360]
(node1) prev[00882328] [00882360,data=2.2] next[00888FC8]
2.2
(root) prev[00888FC8] [00888FC8,data=0] next[00888FC8]
(root) prev[00888FC8] [00888FC8,data=0] next[00888FC8]
1.09