[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
ども・・・・・・・・・・・・和佐田雅史です。
今回はイテレータについてです。
イテレータと言うのは反復子の事で、listやmapコンテナをfor文でポインタによる参照( のようなもの )を行いたい時に使います。
std::list< T >::iterator it = List.begin();
と言う感じですね。
しかし、自分でコンテナを作った時にiteratorはどう作ればよいでしょうか?
基本的には定義したコンテナクラスの中にローカルクラスとして定義するのが一般的です。
template< class T > class Freelist
{
// 実装
// ローカルクラスとして定義
class iterator : public std::iterator<.....>
};
ですが一々コンテナを作る時に定義するのははっきり言ってめんどくさいのでtemplateで作っておくと便利です。
//============================================
// コンテナ作成時の反復子
//
//
//
//============================================
#ifndef _ITERATOR_HPP_
#define _ITERATOR_HPP_
#include <iterator>
namespace Alumina
{
// friendの為の仮定義
template< typename Type , typename value_type > class iterator;
template< typename Type , typename value_type > class const_iterator;
//=============================================
// 通常iterator
//
//
//=============================================
template< typename Type , typename value_type > class iterator : public std::iterator<std::bidirectional_iterator_tag, value_type, void>
{
public:
iterator() : _ptr(0) {}
iterator(Type* ptr) : _ptr(ptr){}
// ジェネリック要素へのアクセス
Type& operator*() const {return *_ptr;}
Type* operator->() const {return _ptr;}
// 真偽判定
bool operator==(const iterator& rhs) const {return _ptr == rhs._ptr;}
bool operator!=(const iterator& rhs) const {return _ptr != rhs._ptr;}
//=============================================
//
// 演算子によるポインタ演算
//
//=============================================
iterator& operator++() {_ptr = _ptr->_next; return *this;}
iterator& operator--() {_ptr = _ptr->_prev; return *this;}
iterator operator++(int i) { iterator temp = *this; ++*this; return temp; }
iterator operator--(int i) { iterator temp = *this; --*this; return temp; }
protected:
// メンバ
Type* _ptr;
friend class Type;
friend class const_iterator;
};
//===============================================
// const_iterator
//
//
//===============================================
template< typename Type , typename value_type > class const_iterator : public std::iterator<std::bidirectional_iterator_tag, value_type, void>
{
public:
const_iterator() : _ptr(0){}
const_iterator(const iterator& it) : _ptr(it._ptr) {}
const_iterator(const Type* ptr) : _ptr(ptr) {}
const Type& operator*() const {return *_ptr;}
const Type* operator->() const {return _ptr;}
bool operator==(const const_iterator& rhs) const {return _ptr == rhs._ptr;}
bool operator!=(const const_iterator& rhs) const {return _ptr != rhs._ptr;}
//=============================================
//
// 演算子によるポインタ演算
//
//=============================================
const_iterator& operator++() {_ptr = _ptr->_next; return *this;}
const_iterator& operator--() {_ptr = _ptr->_prev; return *this;}
const_iterator operator++(int i) { const_iterator temp = *this; ++*this; return temp; }
const_iterator operator--(int i) { const_iterator temp = *this; --*this; return temp; }
protected:
const Type* _ptr;
friend class Type;
};
}
#endif
長くなりましたが、使用例を以下に示します。
// LISTNODE 型を管理するリストコンテナ
template < typename LISTNODE , template< class > class Interfase = ListType > class FreeList
{
public:
// 実装インターフェース設定
typedef Interfase< LISTNODE > LIST;
// それに伴った反復子を定義
typedef Alumina::iterator< LIST , LIST > iterator;
typedef Alumina::const_iterator< LIST , LIST > const_iterator;
.........................
};
このように定義するだけでiteratorが使えるようになります。
便利ですね。
それではお疲れ様でした。
ども・・・・・・・・・・・・・・・・・・GES会長の和佐田雅史です。
今日は新世界の技術と呼ばれているtemplateMetaProgrammingについてです。
少し前にGenericProgrammingについて触れましたが、それの応用です。
GenericProgrammingは型引数というものを持ち、型に依存しない素晴らしいプログラムが組めるというものでした。
// ラジアン
template< typename T > inline T Radian( T Degree )
{
return static_cast< T >( PI/180.0f)*Degree;
}
このようにするとT型へ勝手にラジアン変換してくれる便利なユーティリティの出来上がりです。
float Angle = Radian< float >( 60.0f );
double Angle = Radian< double >( 60.0f );
のように型引数で指定すると勝手に展開されて、その関数を構築してくれます。
※実際はstatic const float Radian = PI/180.0f;と置いて手動で変換する方が遥かに速いですが。
しかしtemplateで渡せるのはclassや、typenameだけではありません。
int、unsigned( unsigned int )、long等と言った整数値も型として渡すことが可能です。
( VisualC++ 6.0コンパイラならdouble等も渡せます。 )
これを利用しない手はありません。
これを利用すれば、コンパイル時に型生成だけで数値演算を行うことも可能です。
以下を踏まえて階乗を計算するtemplateを実装してみます。
// 階乗計算templateMeta
template< unsigned N > struct Fact
{
enum { Val = N * Fact< N - 1 >::Val };
};
// template特殊化 : 0か1の階乗の場合
template <> struct Fact< 0 > { enum { Val = 1 }; };
template <> struct Fact< 1 > { enum { Val = 1 }; };
// 分りにくい人の為にマクロで呼びさせるに定義
#define FactT( n ) Fact< n >::Val
0の階乗や1の階乗は1なので予めtemplateの特殊化をしておきます。
templateの特殊化とは、この型の場合はこういう動きをするというものを定義する機能です。
Fact( 3 );
とすると3の階乗をコンパイル時に型として生成し、実行時には既に求まっているものとなります。
必ずしも必要な技術では無いですが、学生にして神の領域に挑みたい方はどうぞ。
templateMetaに関しては、boostやLokiというライブラリを解析すれば結構勉強になります。
最もそれらが理解出来るのならば学校に来る必要等無いですが・・・・・・・・・・・・。
それではお疲れ様でした。
ども・・・・・・・・・・会長の和佐田雅史です。
今日はスマートポインタについて講義します。
皆さんはnewやdeleteを知っているでしょうか?
動的に変数の領域を確保する時に
Character Chara = new Character;
とすると変数CharaにCharacter型の領域を作成する事が出来ます。
ようするにcで言うmallocを使っているとお考え下さい。
しかし、newで確保したものは解放しないとメモリリークを起こしてしまいます。
mallocで確保したメモリはfree関数で解放しないといけませんよね?
それと同じ事です。
しかし・・・・プログラマと言えど人間ですのでもしかすると解放処理を忘れてしまうかもしれません。
ですのでスマートポインタ( STLではオートポインタ )という物を使います。
std::auto_ptr< 型引数 > ptrObject;
とすると、参照がなくなった時点で勝手に解放してくれます。
javaのガーベージコレクションとまでは行きませんが便利ですよね?
ただ、中には自分でスマートポインタを作りたいという人が居るかもしれません。
なので一応簡易版をここに乗せておきます。
使いたい人はどうぞ。
※1.動的に確保したObjectのみに適用するようにして下さい
※2.ネームスペース( Wasadaとかptrとか )は自由に書き換えてもらって結構です。
使用例は
Wasada::ptr::auto_ptr< Character > Chara();
Chara->m_hp = 1;
等です。
それではお疲れ様でした。
namespace Wasada
{
namespace ptr
{
// オートポインタ定義
template< typename Type > class auto_ptr
{
private:
Type* _ptr;
friend class Type;
public:
// ptrアクセッサ
Type& operator*() const {return *_ptr;}
Type* operator->() const {return _ptr;}
// コンストラクタ
auto_ptr(const Type* ptr)
{
// this->_ptr = NULL;
this->_ptr = ptr;
}
auto_ptr()
{
this->_ptr = NULL;
this->_ptr = new Type;
}
// デストラクタで勝手に解放
virtual ~auto_ptr()
{
if( this->_ptr )
{
delete _ptr;
}
}
};
}
}
☆ STLのリストコンテナを利用したプログラム
STLはスタンダードテンプレートライブラリと言い、C++に標準で入っているライブラリで、開発効率をかなり上げることが出来ます。
STLを利用する事によって変数の型に依存せずにプログラム組めるようになります。
例で言うとint型のリストもchar型のリストも型が違うだけでアルゴリズムは同じです。そのアルゴリズムの部分だけを組んでおいて後から使用する変数の型を指定するプログラムの組み方がSTLの組み方になります。
今回はリストコンテナと言うものを利用したプログラムを解説します。
<>左のような括弧の中に”型”で指定したリスト構造を勝手に作ってくれる便利なものをしようすることで一々手動でリスト構造を設計すること無く、自動生成をしてくれるものです。
ではまず宣言の仕方から説明します。
例 : 宣言の仕方
// int型のリスト構造を作成するプログラム
#include <conio.h>
#include <list>
// 名前空間stdを使用する
using namespace std;
int main()
{
// 定義
list< int > intList;
// 以下STLによるプログラム
return 0;
}
※作成するリストの型はintは勿論構造体やクラス等の型も指定できるのでリストクラス等を作成する場合などには便利です。
☆ 格納の仕方
値を格納するにはpushbackメソッドを使用します。リストの末尾に要素を追加する事が出来ます。
//----------------------------------------------------
// 0~10までの値を格納する
//----------------------------------------------------
for( int i=0 ; i<=10 ; i++ )
{
// pushbackメソッドで値を格納
intList.push_back( i );
}
※同様にpopbackというメソッドを使用するとリストの末尾の要素を消す事が出来ます。
これで宣言が完了しました。
では次に値を参照するにはどうすればよいのでしょう?
通常ではリストを参照するには基本的にHeadからTailまでをポインタで参照しながら処理します。
ではここでリストの先頭と末尾を取得するメソッドを紹介しましょう。
// int型のリストコンテナの要素を参照する物
list< int >::iterator i_IntIterator;
// リストの先頭を取得します
i_IntIterator = intList.begin();
// リストの末尾を取得します
i_IntIterator = intList.end();
☆ 格納した値を参照するには?
格納した値を参照する時はiterator( イテレータ )と言うものを使います。
イテレータとは反復子と呼ばれ、ポインタのような動きをします。
宣言は以下のようにします。
list< int >::iterator 変数名;
// イテレータを宣言する : ポインタみたいなもの
list< int >::iterator i_IntIterator;
以上の事を踏まえると、以下のようなループになると思います。
// イテレータの参照が先頭から末尾で無いときイテレータは次を参照続ける
for( i_IntIterator = intList.begin() ; i_IntIterator != intList.end() ; i_IntIterator++ )
{
printf("%d\n" , ( *i_IntIterator ) );
}
※ ( *i_IntIterator )とはどういうことなのか?
ポインタが参照している値を弄るには*p = 10のようにアスタリスクを付けます。
同じように、( *i_IntIterator ) = 10とすると、
int型のイテレータが参照しているものの中身に10という値を代入する
という事になります。
因みにですが、変数のアドレスがほしい場合は
&( *i_IntIterator )
のような記述の仕方になります。
イテレータはポインタと同じような動きはしますが、ポインタではない(正確にはメンバでint型のポインタオブジェクトを保持している)ので直接アクセスする事が出来ません。
なので、
イテレータの中に格納されている変数の中身のアドレス
と言うように記述してやらないと、構文エラーになります。
以上でlistコンテナの説明は終わりです。
他のvectorやmapコンテナ等も同じようにiteratorを利用する事が多いので触る予定の人は今のうちに慣れておいたほうが良いでしょう。
因みにですが、これが使えるプログラマと使えないプログラマでは開発における効率の差が100倍近くあります。
業界でC++が必須の技術となっているので必ず使えるようになっておきましょう。
それでは今日の 講義はここまでとします。お疲れ様でした。