クラスまみれのゲームプログラミング入門

12:タスク処理を実装する(2)

 C++を使っている人に朗報です。C言語アルゴリズムの教科書と格闘しながら自力で双方向リストを開発する必要はありません!

 C++を扱う開発ソフトには、普段からよく利用されるアルゴリズムをクラス化したプログラム――STL(スタンダード・クラス・ライブラリ)――を標準でサポートするよう、ANSIやISOの規格団体によって定められています。このアルゴリズムの中にはリストアルゴリズムも含まれていますので、これを利用しない手はありません。STLについて完全に解説しようとすると、A4サイズ350ページ分の書籍ができあがってしまうくらいのボリュームになってしまうので、細かいところは専門書に任せるとして、ここでは「リスト」に焦点を絞って解説していきます。

まずはリストアルゴリズムをサポートしたヘッダファイルをインクルードします。

listhead.cpp
#include <list>

 ヘッダファイルなのに「.h」がないことに違和感を覚えるかもしれませんが、C++専用の標準ヘッダファイルには「.h」をつけないのが慣例となっています。旧バージョンのVisual C++では「.h」付きのヘッダファイルも用意されていましたが、Visual C++ 2005以降では.hをつけるとコンパイル時にエラーになってしまいます。

 STLは、STL以外で用いられる宣言との混同を避けるため、「std::list」のように、「std」という通常の宣言と区別するための名称が付け加えられています。普通ならことあるごとに「std::~」と記述しなくてはなりませんが、少し面倒です。そこで、次の一行を加え、「特に問題がないときは、必要に応じて『std::』を付け加えてください」という宣言を行うことで、ひとつひとつの変数や関数に「std」を記述する手間を省くことにしましょう。

namespace.cpp
using namespace std;

 STLでサポートされるアルゴリズムは「テンプレート」という概念によって、様々な形式のデータを柔軟に扱うことができます。listに続いて「<>」を記述し、そこにデータ型を挿入すると、そのデータ型を格納するためのリスト基盤が完成します。

templete.cpp
#include <list>
using namespace std;

list<int> intlist;		// int型のリスト
list<double> dbllist;	// double型のリスト
list<char*> chrptrlist;	// char*型のリスト

// 構造体の宣言
struct POINT
{
	long x;
	long y;
};

list<POINT> ptlist;		// POINT構造体のリスト

 リストはそのアルゴリズムの特性上、data[5]のような配列みたいに、数値を直接指定して抜き出すことはできません。特定の場所にあるデータを参照するには、イテレータという概念を利用します。このイテレータにリストの現在位置を格納することで、リスト内の好きな場所を参照することが可能となり、また、イテレータの前にアスタリスク「*」を付け加えることで、その場所にある実際の値を取得することができます。

iterator.cpp
#include <iostream>
#include <list>
using namespace std;

void main()
{
	list<int> intlist;	// int型のリストを作成

	for(int n = 0; n < 5; n++){
		intlist.push_front(n);	// リストの先頭にnを追加
	}

	list<int>::iterator i;	// int型リストのイテレータを宣言

	// for文を使ってリスト内の値をすべて取得する例。
	// イテレータが示すのは「場所」であり、「値」ではないので
	// 「i < intlist.end()」という記述はエラーになる。
	// i++で、イテレータiは次の場所へと更新されるが、
	// 「i = i + 3」「i += 2」のようにとばすことはできないので注意。
	for(i = intlist.begin(); i != intlist.end(); i++)
	{
		cout << (*i) << endl;
	}
}

 最後にリストでよく使用される命令を一通り利用したサンプルプログラムをリストイメージとともにご紹介します。ほかにもいろいろと関数が用意されているので、実際にプログラムを組んでみて体感してみるとよいでしょう。

listtest.cpp
#include <iostream>
#include <list>
using namespace std;

list<int> intlist;

// リストに格納されている値を列挙する
void EnumList()
{
	list<int>::iterator i;
	for(i = intlist.begin(); i != intlist.end(); i++)
	{
		cout << *i << " ";
	}
	cout << endl;
}

void main()
{
	intlist.push_front(1);	// リストの先頭に1を追加(1)
	intlist.push_front(2);	// リストの先頭に2を追加(2)
	intlist.push_front(3);	// リストの先頭に3を追加(3)
	intlist.push_back(4);	// リストの最後に4を追加(4)
	intlist.push_back(5);	// リストの最後に5を追加(5)
	intlist.push_back(6);	// リストの最後に6を追加(6)
	
	EnumList();

	// リストに格納されているデータの数を表示
	cout << intlist.size() << endl;

	intlist.pop_back();		// リストの最後を抜き出す(7)
	intlist.pop_front();	// リストの先頭を抜き出す(8)

	EnumList();

	list<int>::iterator i = intlist.begin();
	for(int n = 0; n < 2; n++) i++;	// 場所を先頭から2つ進める(9)

	cout << *i << endl;		// その場所にある値を表示

	intlist.erase(i);		// iの場所にあるデータをリストから削除(10)
	intlist.remove(1);		// 値が「1」のデータをリストから削除(11)

	EnumList();
}

(リスト構造のイメージ)
(1)(2)(3)(4)
 
(5)(6)(7)(8)
 
(9)(10)(11)