火曜日, 2月 26, 2008

地頭力を鍛える

ひさしぶりの自己啓発書でしたね。
内容は非常に面白いもので何回も読む必要がある本ですね。
むかし読んだロジカルシンキングの内容もかなり登場し数学的思考である
「問題の抽象化」→「抽象化問題解決」→「定理化」→「具体的問題解決」
というシーケンスも登場していましたね。

具体的に書けばこの本は
フェルミ推定と言われる有名な考え方について解説した本です。
フェミル推定がどんな考え方であるかは本を読めば分かるので
その内容は伏せますが、この本を読むと一つの考え方を知ることができます。
この考え方を応用できるかどうかは今後の自分次第ですが
それでも自分の考え方を広げるきっかけ、
または再認識するにはとっても良い本ではないでしょうか?
随所、随所に登場する当たり前の事はこの手の本では良くある事ですが
自分がそれを実践できるかどうかは別の問題です。

最後に私が最初に読んだ問題を書いておきます。
「日本に存在する郵便ポストの数はいくつか?」

フェミル推定はこの問題を解くことができる考え方です。
一度読んで実践してみてはどうでしょうか

月曜日, 2月 25, 2008

圧勝ですが

先週は土曜日京都でカジノドライブのデビュー戦がありました。
結果から言えば、圧倒的1番人気にこたえる
圧倒的なパフォーマンスでの勝利という結果でしたが、
以外にも時計が平凡な時計でした。

まあ、時計は展開に左右されるものですから
一概には平凡な時計だから弱いという事はありませんが
あの展開であれば時計でも圧倒的なパフォーマンスで勝利して欲しかったです。

この後は、予定通り戦いの場所をアメリカに移し
ベルモントステークスを目指すようです。

しかし、主戦場がアメリカなのであれば
やっぱり日本に入厩した理由が良く分かりませんね。
日本馬でベルモントS制覇という偉業を達成したかったのですかね。
どちらにしても相手が弱かったとか、日本で走るべきだったなどの
雑音が出ないように頑張って欲しいですね。

C++解説14(クラスについて5)

1. 友達を作ろう

今まで、クラスについていろいろと説明をして来ましたが、
最後にフレンド関数、フレンドクラスについて説明します。
名前の通り、友達関数、友達クラスという事になりますが
実際にどのような機能になるかというと、非公開情報に対して
友達だけには公開してあげようという概念になります。

--------------------------------------------------------------------------------
#include "iostream"


class Human {
private:
int age_;
public:
Human(int InitAge) : age_(InitAge) {}
int getAge(void) { return age_; }
friend void AddAge(Human&);
};

void AddAge(Human& obj) {
obj.age_++;
}

int main(void) {
Human obj(10);
std::cout << "Age = " << obj.getAge() << std::endl;
AddAge(obj);
std::cout << "Age = " << obj.getAge() << std::endl;
}
--------------------------------------------------------------------------------
実行結果
--------------------------------------------------------------------------------
Age = 10
Age = 11
--------------------------------------------------------------------------------

というようにfriendで宣言された関数はクラスのprivate変数に対してのアクセスを許可されています。
friendは関数だけではなく、クラスそのものも友達として宣言する事ができます。

class Human {
private:
public:
friend class sample;
};

このように記述されている場合はsampleはHumanのprivate変数にも関数にもアクセスする事が可能となります。

friendは簡単な文法の癖して以外と難しいのではないかと思っています。
どれをfriendとするかという問題には私Sennaは常にぶち当たっています。
friendとする多くの関数やクラスにfriendとする意味を感じないのです。
上記にはfriend関数の例を記述していますが、はっきり言ってfriendとして記述する必要性はまったくありません。
従って、あまり言い例とお世辞にも言えないのです。

クラス間の関係を精査しどれがfriendクラス、関数となるのか
また、必要がないのか?などど言った問題を整理してからfriendというキーワードを使用する事にしましょう。

クラス説明については一応終了です。

土曜日, 2月 23, 2008

C++解説13(クラスについて4)

1. コピーコンストラクタ

クラスの勉強をする時にきちっと勉強をしないとクラスの文法、継承、コンストラクタ、デストラクタというところまでは
共通して行われるようですが、コピーコンストラクタが抜ける勉強をする人を多く見かけたりします。
何でこのような事態が発生するのか疑問に思い、本屋に駆け込んだところ本によってはコピーコンストラクタを
クラスとは離れたところで説明している本、又はサイトを多く見かけました。これは結構頂けない事だと思います。
C++を勉強している多くの人はC言語を勉強してから始める傾向にあるようです。
そのため、クラスを覚えるとクラスを実際に使い始め関数に渡す事をしたり関数からクラスポインタを戻り値として受け取ったりといろいろな事をやり始めます。
しかし、そこにはコピーコンストラクタという技術が沢山使われておりしらない箇所で危険な処理が動いています。

ここでは、そのような事を防ぐためにコピーコンストラクタの存在についてと実際にどのような時に動く事になるのかなどを検証していきます。

--------------------------------------------------------------------------------
#include "iostream"

class Sample {
public:
Sample() { std::cout << "sample::sample()\n"; }
void View() { std::cout << "sample::View()\n"; }
~Sample() { std::cout << "sample::~sample()\n"; }
};

void func(Sample inObj) {
inObj.View();
}

int main(void) {
Sample obj;

func(obj);

return 0;
}
--------------------------------------------------------------------------------
実行結果
--------------------------------------------------------------------------------
sample::sample()
sample::View()
sample::~sample()
sample::~sample()
--------------------------------------------------------------------------------

デストラクタが2回動いている事が分かると思います。これは、C言語を勉強している人であれば問題ない事実でしょう。
関数func()が終了した時に引数のクラスはスコープ外となりデストラクタが動いているのです。
「!?だったらコンストラクタは何処で動いているの!?」

間違いなく、func()が動く前にコンストラクタが動いています。
ここで動くのがコピーコンストラクタという事になります。

コピーコンストラクタの文法です。
class Sample {
public:
Sample(); // コンストラクタ
Sample(const Sample&); // コピーコンストラクタ
};

基本的にコンストラクタと同じです。
クラスの参照を引数として受け取るコンストラクタという事になります。
これを名にコピーコンストラクタと呼んでいます。
--------------------------------------------------------------------------------
#include "iostream"

class Sample {
public:
Sample() {
std::cout << "(" << this << ")sample::sample()\n";
}
Sample(const Sample& obj) {
std::cout << "(" << this << ")sample::sample(cosnt Sample& obj)\n";
}
void View() {
std::cout << "(" << this << ")sample::View()\n";
}
~Sample() {
std::cout << "(" << this << ")sample::~sample()\n";
}
};

void func(Sample inObj) {
inObj.View();
}

int main(void) {
Sample obj;

func(obj);

return 0;
}
--------------------------------------------------------------------------------
実行結果
--------------------------------------------------------------------------------
(0012FF84)sample::sample() // オリジナルのコンストラクタ
(0012FF54)sample::sample(cosnt Sample& obj) // コピーコンストラクタ
(0012FF54)sample::View() // コピーのView
(0012FF54)sample::~sample() // コピーのデストラクタ
(0012FF84)sample::~sample() // オリジナルのデストラクタ
--------------------------------------------------------------------------------

という事になります。
じゃあ、何でコピーコンストラクタは重要なのでしょうか?
「別にデストラクタが2回動く事ぐらい問題ではないんじゃないの!」
と思ったあなた、C言語から勉強し直して下さい!。
こいつはめちゃくちゃ大きい問題です。
C言語で言うと、mallocしたアドレスを2回freeしている事になります。もう、問題って分かりましたよね。
実際に、コンストラクタでアドレスを確保してデストラクタで開放するプログラムを作成して実験して見ます。
--------------------------------------------------------------------------------
#include "iostream"
#include "cstdlib"
#include "cstring"

class Sample {
private:
char *cp;
public:
// どのオブジェクトか不明のためアドレス情報を出力
Sample() {
std::cout << "(" << this << ")sample::sample()\n";
cp = (char*)malloc(5);
strcpy(cp, "test");
}
void View() {
std::printf("(%p)sample::View()_%p_%s\n", this, cp, cp);
}
~Sample() {
std::cout << "(" << this << ")sample::~sample()\n";
free(cp);
}
};

void func(Sample inObj) {
inObj.View();
}

int main(void) {
Sample obj;

func(obj);
obj.View();

return 0;
}
--------------------------------------------------------------------------------
実行結果
--------------------------------------------------------------------------------
(0012FF88)sample::sample()
(0012FF5C)sample::View()_00913A30_test // cpのアドレスに注目
(0012FF5C)sample::~sample() // ここでフリーされてしまう
(0012FF88)sample::View()_00913A30_<・A // なんじゃこりゃ!
(0012FF88)sample::~sample()
--------------------------------------------------------------------------------

コピーのデストラクタが動いているんも関わらず、オリジナルのアドレスがフリーされてしまいます。
freeされたchar*にアクセスしているため、このような事象が発生してしまいます。
この内容から、コピーコンストラクタが存在しないクラスにおいて
クラスのコピーとは、バイナリーレベルでのコピーが発生している事が推測できます。


そこでコピーコンストラクタによって、同じ内容を格納する新しいアドレスを確保する必要性が出てきます

--------------------------------------------------------------------------------
class Sample {
private:
char *cp;
public:
// どのオブジェクトか不明のためアドレス情報を出力
Sample() {
std::cout << "(" << this << ")sample::sample()\n";
cp = (char*)malloc(5);
strcpy(cp, "test");
}
Sample(const Sample& obj) {
std::cout << "(" << this << ")sample::sample(cosnt Sample& obj)\n";
cp = (char*)malloc(strlen(obj.cp));
strcpy(cp, obj.cp);
}
void View() {
std::printf("(%p)sample::View()_%p_%s\n", this, cp, cp);
}
~Sample() {
std::cout << "(" << this << ")sample::~sample()\n";
free(cp);
}
};
--------------------------------------------------------------------------------
実行結果
--------------------------------------------------------------------------------
(0012FF88)sample::sample()
(0012FF5C)sample::sample(cosnt Sample& obj)
(0012FF5C)sample::View()_00913A40_test // コピー用のアドレスとなっている
(0012FF5C)sample::~sample() // コピーのデストラクタ
(0012FF88)sample::View()_00913A30_test // コピーがfreeされても問題なし
(0012FF88)sample::~sample()
--------------------------------------------------------------------------------

という事になります。
コピーコンストラクタの重要性は理解できたと思います。
他にも、クラス型を返却する時などもコピーコンストラクタが動く事になります。
是非、クラス設計を行う場合はコピーコンストラクタの存在を忘れないようにして下さい。

今週末の競馬

またまたやっちゃいました。
今回の京都記念の私自身の予想はアドマイヤ1、2着の予想でした。
しかもかなりの自信あり予想でした。
3着不明、アドマイヤオーラが元ペーパーオーナーの馬である事などを
馬単:アトマイヤオーラ→アドマイヤフジ
馬連:アトマイヤオーラ-アドマイヤフジ
が予想でした。

後は、その時の状況によりますが
3連単の総流しまで考えていました。

が、本日購入前に見せられた馬体重がアドマイヤフジ20キロ増!
一気に危険レースに格下げされ軸馬変更
アドマイヤフジからウォッカに変えました。
全ては、この判断の失敗です。
ウォッカに変更した段階で投資予定金額は大幅に減らしましたが
かなりのショックです。
なかなか上手く行きません。

明日はフェブラリーSです。
ダイワスカーレットの回避でこれまた軸馬不在となってしまいました。
とりあえず、まだまだ検討中なのですが
現在の本命はワイルドワンダーです。
相手候補はロングプライドとドラゴンファイヤー
3連復まで手を広げた場合はデアリングハート、ブルーコンコルド、ヴァーミリアン、クワイエットデイ
ってところが現在の予想です。

火曜日, 2月 19, 2008

世界でもっとも美しい10の科学実験

正直難しかったです。
私の知識では凄いというところの理解が
おそらく行き届いていないのが現実な気がします。

電子の話になると途端に分からなくなったので
後半に書かれている内容については
頭の中をスルーしている気がします。

ただ前半は地球や重力など受け入れられる内容だったため
それなりに理解できたと思います。

面白かったかという質問に関しては、
面白くなかったとしか答えられないかもしれません。
が、あくまでも私自身の知識ではって事にはなりますが。

毒を売る女

島田荘司さんの短編集です。
短編集という事でボリューム不足である事は否めません。
これはという作品も特にはありませんでした。

タイトルになっている「毒を売る女」は私自身には訴えるものはなく
作品の中で一番好感を持てたのが「糸ノコとジグサグ」ですね。

最近行われていたReader's Selectionにも選ばれていたようですね。
ただ、それでも私は短編集が今一好きにはなれない事実は変わりませんね。

一部、推理小説とはかけ離れた作品ですが「バイクの舞姫」という作品がありました。
島田荘司さんが手がける作品の中では異例の作品のような気がします。
この辺りはファンの心理としては貴重な作品を読んだって感覚はありましたね。

C++解説12(クラスについて3)

1. 継承

クラスの継承について説明します。
継承を行う要因として主に3種類存在すると言われています。

1.1 拡張継承
拡張継承とはどの本でも一番最初に説明がされている継承です。
「人間は哺乳類で...」などど書かれている本があればまさしく拡張継承の説明でしょう。
まあ、今更「人間は哺乳類で...」何て説明どの本もしてないかもしれませんが
ようするに、あるクラスでは機能が不足しているため
追加機能を実装する場合に取る、継承の方法です。

1.2 データ継承
具体的には基底クラスにprotectedで定義した変数を
派生クラスがprivate変数として使用する事を指します。
まさに、データのみを継承するという事になります。
これはそのままですが、いろいろと有効に活用できます。

1.3 インターフェース継承
最後にインターフェース継承です。
オブジェクト指向で多態性(ポリモフィズム)という言葉があります。
C++にはオーバーライドという言葉とオーバーロードという言葉があります。
オーバーライドとはポリモフィズムそのものを指し、
オーバーロードとは同じ名前で宣言されている関数を
引数や返却値などの違いで使い分ける方法です。
ここではオーバーライドの説明を行います。
オーバーロードについては後ほど...


それでは継承の文法です。

class 派生クラス名 : 基底クラス名 {
// 後はクラスの書き方とまったく同じ
}

それでは細かい説明をします。

1.1.1 拡張継承(詳細説明)
「警察官」というクラスを作成してみます。
警察官は人間ですが、人間は警察官ではありません。
なので警察官とは人間の機能に警察の機能を追加する事で
実現できる事になります。

では警察官とはどういった特徴があるかを考えます。

・役職がある
・捜査する
・逮捕する
・拳銃を撃つ

もの凄く簡単ですが、拡張継承の説明としてはこれで十分です。

class Human {
private:
char *Name;
int Age;
char Birthday[9];
public:
Human(char*, int, char*);
~Human();

char* getName(void) { return Name; }
};

class Police : public Human {
private:
int OfficialPosition; // 1:巡査部長 2:警部補 以外:警視
public:
Police(char *InName, int InAge, char *InBirthday, int Position)
: Human(InName, InAge, InBirthday) { OfficialPosition = Position; }
void Search() { std::cout << "捜査してます\n"; }
void Arrest() { std::cout << "逮捕します\n"; }
void ShootGun() { std::cout << "発砲しました\n"; }
char* getOfficialPosition(void);
};

char* Police::getOfficialPosition(void) {
switch(OfficialPosition) {
case 1: return "巡査部長";
case 2: return "警部補";
default: return "警視";
}
}

実際に使ってみましょう。

int main(void) {
Police obj1("semona", 0, "20040101", 1);

std::cout << obj1.getName() << "," << obj1.getOfficialPosition() << std::endl;
std::cout << obj1.getName() << ","; obj1.Search();
std::cout << obj1.getName() << ","; obj1.ShootGun();
std::cout << obj1.getName() << ","; obj1.Arrest();
std::cout << obj1.getName() << "," << obj1.getOfficialPosition() << std::endl;

return 0;
}

ここで注目して欲しいのはPoliceオブジェクトを作成してるにも関わらず、
getName()、というHumanのpublicメソッドを使用しているところです。
これが継承という機能によって実現される事になります。

Police(char *InName, int InAge, char *InBirthday, int Position)
: Human(InName, InAge, InBirthday) { OfficialPosition = Position; }
一応この部分におやっ?っと思った人のために少しだけ解説します。

Police(...) : /* 何か書いてある */ { ... }
この部分ですが、初期化といいます。
初期化と代入の違いは説明しましたが、その初期化に当たります。
何で、こんなところに書くのかを説明したいと思います。

class Base {
public:
Base() { std::cout << "Base::Base()" << std::endl;}
~Base() { std::cout << "Base::~Base()" << std::endl;}
};

class Delived : public Base {
public:
Delived() { std::cout << "Delived::Delived()" << std::endl;}
~Delived() { std::cout << "Delived::~Delived()" << std::endl;}
};

int main(void) {
Delived dObj;

return 0;
}

実行結果
Base::Base()
Delived::Delived()
Delived::~Delived()
Base::~Base()

派生クラスのインスタンスを作成したところ、
基底クラスのコンストラクタとデストラクタがしっかり動いていますね。
しかも、コンストラクタは先に、デストラクタは後に...
これはC++の仕様になります。
つまり、派生クラスは自分のコンストラクタ実行時には
基底クラスをきちっとした形で使用できるという事になります。
でも、基底クラスがこんな感じになっていた場合どっちが動くの?
プログラムは分からない場合は何でも実験です。やってみましょう。

class Base {
public:
Base() { std::cout << "1:Base::Base()" << std::endl;}
Base(int i) { std::cout << "2:Base::Base()_" << i << std::endl;}
~Base() { std::cout << "Base::~Base()" << std::endl;}
};

実行結果
1:Base::Base()
Delived::Delived()
Delived::~Delived()
Base::~Base()


予想通りの結果ですかね。
待って下さいよ。わたくしは2:Baseを使用したいのですが...
でもDelivedのコンストラクタが動くときには終わっちゃってるし
何か、見えてきたって感じですね。
何のためにこんな説明をしているのか!そうですよ初期化ですよ
という事で派生クラスをちょこっと変更

class Delived : public Base {
public:
Delived() : Base(3){ std::cout << "Delived::Delived()" << std::endl;}
~Delived() { std::cout << "Delived::~Delived()" << std::endl;}
};

実行結果
2:Base::Base()_3
Delived::Delived()
Delived::~Delived()
Base::~Base()


他にもこんな使い方が
class Base {
private:
const int CONST_INT;

...
};

このようにconstで宣言された変数には代入は行えません。
しかし代入はできないのですが初期化は当然できます。
だから「Base() : CONST_INT(1) {}」こんな感じで初期化します。
もちろん、こんな初期化が許されるのは、コンストラクタだけです。
理由は簡単ですね。コンストラクタがクラスの初期化だからです。

少し脱線しましたが、以上が拡張継承です。
ここでは人間機能しかなかったHumanというクラスに
Policeというクラスを拡張した警察官というクラス作成しました。
Humanクラスを拡張したため、人間の機能も持っているわけですね。
もし、Policeクラスは基底クラスに犬クラスを選択すれば警察犬ってところですね。


1.2.1 データ継承(詳細説明)

人間ってクラスを作ってきましたが、そもそも人間って
こんな簡単でしょうか?(身も蓋もねえ事、言ってんじゃねえよ!)
すいません。例が無くって、でも進めましょう。

例えば、人間とは男と女に分けられます。
でもって男と女とはまったく違うものです。
それを人間なんて一つのクラスにしてしまったらこの違いをどう表現すればいいのでしょうか?
一つに拡張継承を使って、人間クラスを継承する男クラスと女クラスを作成するという表現方法があります。
しかし、ここではデータ継承を使って表現したいと思います。
今まで、人間クラスで管理していた名前、年齢、誕生日といった項目は人間であれば誰もが持っている項目です。
これらを人間クラスのデータをして保持する事には何も変化はありません。
今までは
class Human {
private:
char *Name;
int Age;
char Birthday[9];
};

として保持し、ここにアクセスするメソッドを提供するという方針でした。
データ継承とはアクセスするメソッドを提供する代わりに、直接継承したクラスにはアクセス権を与える継承方法となります。

クラスにはprivate(非公開情報)、public(公開情報)という他に
protected(継承先には公開、非継承先には非公開)という宣言方法が存在します。
つまり、今までprivateで宣言してきた変数達をprotectedで宣言してやると
いう事ですね。

class Human {
protected:
char *Name;
int Age;
char Birthday[9];
};

これだけですね。すると拡張継承ではPoliceクラスがHumanクラスを継承したので
同様にPoliceクラスにHumanクラスを継承させましょう。

class Police : public Human {
private:
int OfficialPosition; // 1:巡査部長 2:警部補 以外:警視
public:
// Humanの変数には直接アクセスできるため、このような初期化は不要
// Police(char *InName, int InAge, char *InBirthday, int Position)
// : Human(InName, InAge, InBirthday) { OfficialPosition = Position; }
// 新しいコンストラクタ
Police(char *InName, int InAge, char *InBirthday, int Position) {
Name = (char*)malloc(strlen(InName));
strcpy(Name, InName);
Age = InAge;
strcpy(Birthday, InBirthday);
OfficialPosition = Position;
}
};

こんな感じになります。直接アクセスする分すっきりした感じがしますね。
あんまり、難しい話ではないと思います。
ただ、私が考える注意点だけ書いておきます。このデータ継承にはいろいろな使い方があります。
しかし、データ継承を行う場合はデータのみを継承する事をお勧め致します。
データ継承を行い、クラスの機能の継承する何て事をやり出してしまったら
おそらく、自分の頭の中でパニックを起こしてしまう事でしょう。


最後はインターフェース継承です。
ここの冒頭で書いたように、こいつはクラス最大の恩恵を授かる事ができるでしょう。
少しだけ、C言語でこいつの実現を考えて見る事にしてみます。
そもそも、インターフェースプログラミングとは何でしょうか?
実は、使用する側にはインターフェースのみ意識させクラスの中身は考えさせない。
と言ったカプセル化の概念の究極の行き先ではないでしょうか?

たとえば、車を作る事を考えてみましょう。
セダン、RV、ワンボックスいろいろとありますが、要は車を作るという事です。
いろいろと部品の違いはありますが、車を作るという肯定は何も変わりません。
そうです、車を作るというインターフェースに変更はないのです。
だから、「車を作る」というメソッドのインターフェースを使ってセダン、RV、ワンボックスといろいろ作れればいいという事になります。
では実際にどのようなものか、プログラムを書いてみます。

はじめにインターフェースとなるクラスを作成します。

class CarFactory {
public:
virtual void CarCreateExec() = 0;
};

これで作成終了です。
実際に新しい概念が2つ程あります。
1つ目は「virtual」という修飾子がメソッドの前にある事
2つ目はメソッドの後ろに「= 0;」という意味不明な箇所がある所です。

まず、1つ目のvirtualとは仮想関数(バーチャルメソッド)である事を宣言しています。
仮想関数とはこいつはオーバーライドできますよ!って事になります。
オーバーライドとは、直訳すれば「取って代わる。無視する」などの意味となりますがここでは受身で取って下さい。
つまり「取って代えられる、無視される」って感じでしょうか
そして2つ目の「=0;」ですが、純粋な仮想関数である事を指しています。
純粋な仮想関数?
当然、こうなっても仕方ないですわね。
はじめに、仮想関数を説明してから純粋仮想関数について考えてみますね。

とりあえず、さっきのCarFactoryを拡張しましょう。

#include "iostream"

class CarFactory {
protected:
char *CarName;

public:
void PreCarCreate() {
std::cout << "車を作り始めます\n";
}
virtual void CarCreateExec() {
char *CreateCarName = "自動車1";
CarName = CreateCarName;
}
void SufCarCreate() {
std::cout << CarName << "の完成です\n";
}
};

int main(void) {
CarFactory *obj = new CarFactory();

obj->PreCarCreate();
obj->CarCreateExec();
obj->SufCarCreate();

delete obj;

return 0;
}

実行結果
車を作り始めます
自動車1の完成です


まあ、何て事はないですよね。
では次に「virtual」で宣言されている関数をオーバーライドします。

#include "iostream"

class RvCarFactory : public CarFactory {
public:
virtual void CarCreateExec() {
char *CreateCarName = "RV車1";
CarName = CreateCarName;
}
};

int main(void) {
CarFactory *obj = new RvCarFactory(); // ここがポイント

obj -> PreCarCreate();
obj -> CarCreateExec();
obj -> SufCarCreate();

delete obj;

return 0;
}

実行結果
車を作り始めます
RV車1の完成です

こんな結果になります。理解できましたかね? すべてのポイントは継承なんですが...
派生クラス(継承したクラス)は基底クラスの公開関数/変数、
継承先公開関数/変数を自分のものとか、言ってきましたが正しくは派生クラスの中に基底クラスの内容が含まれるという言い方が正しいかもしれません。

class Base { ... };
class Deli1 : public Base { ... };
class Deli2 : public Deli1 { ... };

となっている場合、

class Base
Baseクラスの内容

class Deli1
Baseクラスの内容 + Deli1クラスの内容

class Deli2
Baseクラスの内容 + Deli1クラスの内容 + Deli2クラスの内容

こんなイメージです。でっかく×2なっていっているという事です。
で今回ポイントになっている以下の文ですが、
CarFactory *obj = new RvCarFactory();

上の例でいくと
Base *obj = new Deli1();
という事になりますね。でどのような意味になるかというと*objには間違いなくDeli1の内容が格納されています。
しかし、objとはBaseポインタ型です。という事は、Baseクラスが提供している方法でしか
Deli1にアクセスできないという事になってしまいます。
でも間違いなくDeli1クラスのオブジェクトを作成しているため「RV車1の完成です」という結果になったのです。
Deli1のCarCreateExecは「RV車1」のアドレスを代入しているでしょ!

何か、いろいろ言ってるけど使えなくね~~~!。なんて思ってしまったら「ばかもん!」って言われるのですよ。

ここが最大のポイントですから。ここって何の説明でしたっけ?そうですよ。インターフェースプログラミングですよ。
インターフェースプログラミングとはインターフェースを基底クラスに宣言して、そのインターフェースを守って派生クラスを実装するって事ですよ。

今回はオーバーライドを説明するために、基底クラスに関数の実装を用意しましたが、
RV車を作成する特化したクラスがあったのですから自動車、ここではセダンだとしますね。
自動車を作成する特化したクラスがあれば良いという事になります。
またCarFactoryクラスはインターフェースに特化させてしまえば関数の実装は不要という事になります。

この「インターフェースに特化させるため、関数の実装は行いません」
という宣言を純粋仮想関数で行います。

つまり、以下のクラスのインスタンスは作成を行う事はできません。
なぜなら、CarCreateExecには関数の実装が行われていないため
実際にどれを動かせば良いのか分からないという事になるからです。
ここまでの内容を踏まえて、RV車とセダン車を作成するクラスを作成します

// インターフェース専用です
class CarFactory {
public:
virtual void CarCreateExec() = 0;
};

class RvCarFactory : public CarFactory {
public:
void CarCreateExec() {
std::cout << "RV車を作成しました\n";
}
};

class SedanCarFactory : public CarFactory {
public:
void CarCreateExec() {
std::cout << "セダン車を作成しました\n";
}
};

int main(void) {
// ここではセダン車を作成しますが、ここ1行を書き換えてしまえば
// RV車を作成する事ができます
// CarFactory *obj = new RvCarFactory();
CarFactory *obj = new SedanCarFactory();

obj -> CarCreateExec();

delete obj;

return 0;
}

実行結果
セダン車を作成しました


結構いろいろな事が思いついてきたでしょ?
ここでは、簡単なインターフェースプログラミングについて触れました。
こいつをものにする事ができればC言語とは違うまったく新しいC++の世界が見えてくると思います。

余談ですが、デザインパターンと言われているパターンを実装する場合
このインターフェースプログラミングは必須な知識/テクニックです。

金曜日, 2月 15, 2008

IPOD TOCH

IPOD TOUCHってめちゃくちゃかっこいいですよ。
16Gで容量が足りなかったため見送っていましたが
32Gの発売で購入に踏み切りました。

まだまだ使いこなしてはいませんが、音楽を聴く上での
操作等は問題ないですね。
前のIPODと比べると、ホイールではなくなったため
感覚での操作は行えなくなりましたが
まあ、そこまで大きい問題ではありません。

家の無線LANがWiFi規格ではないので総取替えする予定です。
その前にFONなどが使えないかどうかを調査中です。

今のところかっこよさもあるので最高ですね。

火曜日, 2月 12, 2008

家が全滅

子供が3人とも熱を出してしまい、全滅状態です。
最初は1人だったのですが、今の状況ではどうしてもうつってしまいます。
週末は家に居るように心掛けようとも思いましたが
逆にストレスになってしまったところもあるようで
ご飯を外で食べたいとの事だったため食事に出かけました。

本人達は満足したようですが、風邪の状況は良くなる事はありませんでした。
今日で4日目になります。
長女は回復傾向にありますが、次女は悪化しているような気もします。
最悪なのが、3ヶ月にも満たない三女が熱っぽいところですね。

見ているこっちも辛いので早く治ってくれる事を祈るのみです

木曜日, 2月 07, 2008

C++解説11(dequeとlist)

「deque」と「list」は「vector」と同じ順序コンテナと呼ばれるものです。

「deque」は「vector」に対して配列の先頭に要素加する事も
高速で実現ができるコンテナで
その他については「vector」と同一と考えて問題ないでしょう。
これだけを書くと「vector」は不必要で「deque」だけ存在すれば良いのではないか?
という感じもします。

実際問題、多くのケースは「deque」だけで問題ないと思います。
逆の言い方をすれば、「vector」だけで
問題ないケースがほとんどかもしれません。
どのようなプログラムを記述するかに依存しますが
配列の先頭に要素追加を行う必要性がないのであれば
「vector」で良いのではないかというのが
私の考え方ではあります。

もし、後で配列の先頭に要素追加を行う必要が存在した場合
「deque」と「vector」の関係であれば、よっぽどの事がない限り
配列の定義部分のみを変更してしまえば動くのではないかと思います。
まあ、この事態は設計ミスという事になりますが。
(言語が設計ミスをカバーするってもの変な話です)

「vector」を使えれば、間違いなく「deque」は使用できます。
以下に違いを示しますが大きい問題ではない事が分かると思います。

「deque」のみ存在する関数
pop_front() 先頭要素を削除する
push_front() 両端キューの先頭に要素を追加する

「vector」のみ存在する関数
capacity() ベクタが保持できる要素数
reserve() ベクタが保持できる要素数を設定する

相互変更をする場合に上記関数を使用していなかった場合
それぞれ置き換える事が可能という事になります。
「deque」を使用していて「pop_front」「pop_back」を
使用していないのは、ちょっと理解できないですね。

「vector」が準備している「capacity」、「reserve」というものは
ほとんど使わないのではないでしょうか。

「reserve」を使用するのは本当の意味での
高速プログラムを実現する時です。
「vector」は要素を無限(これは言い過ぎですがまあ嘘にはならないかな)
に追加できるのが特徴ですが、その裏では「メモリを確保」して、
「確保したメモリに既存のデータをコピー」してといった
容易に想像できる処理が動いています。

しかし、この確保するメモリを多くすれば拡張回数を減らす事ができます。
かと言って、多すぎるメモリの確保は
獲得コストが必要以上に要する事態が発生します。

つまり通常発生するデータの格納個数をA、
突発的な事故が発生しデータ量が増えるケースを予期した戸数をBとした場合
一番最初に「A」の分だけ「reserve」してしまえば
無駄なメモリの確保、コピーが減るという事になります。
しかも多くの場合、領域の確保、データのコピーというものは発生しないでしょう。
このような対策を「reserve」を上手く利用する事で行える事になります。

しかし、この処理にコストがかかると言っても
何秒といった時間が係るわけでもないため
ここまで性能を追求するプログラムを作成する
機会というのは少ない気もします。

要するに、「reserve」など使う機会がほとんどないですよ!
って事を言いたいのです。
それは「capacity」にも言える事で、私が思うに「capacity」って
一度も使った事が無いかもしれません。
覚えている限りではないです。(練習は除きますよ)

この事から、「vector」から「deque」にコンテナを移す事は
多くの場合で容易にできる事が分かると思います。

「list」は多少2つに比べると特徴に違いがあります。
最大の違いは「ランダムアクセス」不可というコンテナであるところです。
「ランダムアクセス」とは直接アクセスだと考えて下さい。
イテレータのところで扱った「random_iterator」が扱えないという事です。
つまり配列のインデックスを指定する際に「[]」という演算子を指定しますが
「list」にはそれができないという事になります。
これだけでもかなりの特徴があると思います。

vector v;
deque d;
list l;

v[0]=1;
d[0]=1;
l[0]=1; ← 出来ない!

という事です。何故か?というと「list」というコンテナが
「ランダムアクセス」を行われることを前提に設計されていないからです。
「ランダムアクセスを行えるようにする」場合と
「ランダムアクセスを行えなくても良い」場合では
何が違うのかというとメモリが連続領域である必要がないというところです。

つまり配列サイズが10となっている「a」の5番目の要素を削除したい場合
配列を確保するメモリが連続領域である必要のある
「vector」「deque」については、5番目の要素を削除後に
それより後ろに格納されている全てのデータを1つ前に移動させる必要があります。

それに対して「list」は連続領域である必要がないため
「N番目のデータの次はN+2番目である」
「N+2番目のデータの前はN番目である」という
2つの処理を行う事で要素の削除が行えることになります。
逆も同じで配列途中に要素を追加する事を考える場合、
「vector」「deque」は追加する要素位置以降に存在する要素を
全て追加する要素分後ろにずらしてから、要素の追加を行います。
「list」は「N番目のデータの次はM番目である」
「N+1番目のデータの前はM番目である」という
2つの処理を行う事で要素の追加が行えます。

データの追加、削除を配列の途中に頻繁に行う場合、
「list」というコンテナは非常に有用です。
少量のデータであれば、気にするポイントではないかもしれませんが
大量のデータを扱う場合にはパフォーマンスなど
コンテナを選択した段階で決まっているようなものです。
どんなに、高速なプログラムを組んでも
設計が間違っていれば高速にはなりきれないという事です。

扱い方は「vector」「deque」と同じです。
これがSTLの素晴らしいところですね。

水曜日, 2月 06, 2008

C++解説10(iterator)

イテレータとは汎用ポインタです。
っていうと間違っているような気もするのですが
そうやって覚えて問題ないと思います。

イテレータの簡単でかつ最も多様する方法に
STLが提供するコンテナの操作、参照といった事があります。

イテレータにはいくつかの種類が存在し
これを正確に理解する事で単純なバグを抑止する事ができると思います。

1.forward_iterator
前方イテレータや前進イテレータなどと呼ばれているもので
「++」というインクリメントのみの移動が可能なイテレータです。
イテレータの参照、イテレータへの更新といった操作が許されています。
参照ができるという事は比較演算子である「==」「!=」などが使用でき
更新が行えるという事は代入演算子である「=」が使用できるという事です。

2.input_iterator
forward_iteratorに対して、更新系の操作(要するに代入)を奪ったものが
input_iteratorと言います。
このイテレータに対して、代入演算子を使用するとコンパイルエラーとなります。

3.output_iterator
forward_iteratorに対して、参照系の操作を奪ったものが
output_iteratorと言います。
参照系の操作が出来ませんので、if文で使用したり、for、whileなどの終了条件に
使用する事もできません。

4.bidirectional_iterator
forward_iteratorに対して、デクリメントの機能を追加したものが
このbidirectional_iteratorと言い、双方向イテレータと呼ばれます。

5.revese_iterator
forward_iteratorに対して、インクリメントを行うと後方に進むのに対して
このrevese_iteratorはインクリメントすると前方に進みます。
この「reverse_iterator」と「forwaed_iterator」については
どちらのイテレータを使用しているかを認識していないと
進む方向が同じプログラムでも変っている事になります。

6.random_iterator
最後に、全ての機能を兼ね備えるのがrandom_iteratorと言われるものです。
bidirectonal_iteratorに何の機能が加わるかというと「[]」での参照機能です。
つまり「[]」という演算子でアクセスを行えるコンテナについては
使用されているイテレータがこのrandom_iteratorという事になります。


実際に「vector」でイテレータを使用してみます。

vector v;
のイテレータとして
vector::iterator i;
という宣言方法で「i」というイテレータを宣言します。

イテレータの型を特定するのが目的になります。
ポインタ宣言を行う場合に「char*」なのか「int*」なのか
といった事を宣言しています。

これはC言語の特徴の1つと言ってよいかもしれません。
理由は簡単で配列はポインタで表記されるのがC言語です。

int n[10];
char c[10];

というような2つの配列が存在した場合、
0番目の要素と1番目の要素の場所の間にはどのくらいの距離があるのでしょうか?
配列とは連続しているメモリー領域を約束されていますので
0番目の要素と1番目の要素が隣り合わせである事は間違いありません。
0番目にしても1番目にしても通常の「int」として扱う事ができますから
2つ間とは、配列の型のサイズである事が分かります。
つまり移動量を特定するためには、どの型であるかを知る必要があります。
そのため、「int*」「char*」と言った宣言が必要になります。
イテレータもこれと同じ理屈で型が必要という事になります。


前回あいまいのまま終わらせた中に「vector」で使用できる
アルゴリズムが他にも沢山ある事を書きました。
それを解説しながらイテレータというものを実際に使用してみます。


「vector」には「begin」「end」という
最初と最後のイテレータを返却するメソッドが存在します。

これで一番簡単なプログラムは「vector」の要素として管理されている
最初から最後までの全ての要素の値を出力するプログラムです。
それは以下の通り書くことができます。

vector v;
vector::iterator i;

for(i = v.begin(); i != v.end(); ++i) {
cout << *i << ","; } 「*i」などどいう記述はまさにポインタそのものです。 C++では様々なアルゴリズムが登場しますが ほとんどが 「i = v.begin()」~「 i != v.end()」 という考え方が基本となります 要するに「end」が指すのは、最後の位置ではなく 「最後+1」の位置という事になります。 次に「insert」です。 vector v1;
vector v2;

2つの「vector」の配列が存在し、

「v1」の後ろに「v2」を挿入する場合

v2.insert(v1.end(), v2.begin(), v2.end());

と記述する事ができ

「v1」の先頭に「v2」を挿入する場合

v2.insert(v1.begin(), v2.begin(), v2.end());

と記述する事ができます。


また途中に挿入する必要がある場合は

for(vector::iterator i = v1.begin(); i != v1.end(); ++i) {
if (*i == 挿入する配列の後にしたい要素) {
v1.insert(i, v2.begin(), v2.end());
}
}

と記述すれば良いという事になります。
これは本当に素晴らしい事です。
C言語からの進化、又はSTLで記述しないC++プログラム
と比べてもその素晴らしさは圧巻です。

「erase」もまったく同じ考え方で関数そのものは2つの引数で定義されています。

iterator erase(iterator pos);
iterator erase(iterator s_pos, iterator e_pos);
イテレータを指定する事でこのアルゴリズムも一瞬にして手の中に入っていますのです。

今回は「vector」を例に少しだけイテレータの世界に入りました。

火曜日, 2月 05, 2008

C++解説9(vector)

今回はSTL(Standard Template Library)を使用します。
最初は一番馴染み易いものが良いのでvectorという
シーケンスコンテナを扱ってみます。

「vector(べクター)」とは可変長配列を指します。
簡単に言えば、配列を宣言する上で必要だった
要素数のMAXを指定する事なく
いくらでも要素を追加する事を可能にした配列です。

・最大の特徴はランダムアクセスが可能である
・末尾へのデータ追加、削除については「O(1)」を出す。
・C言語の配列と同様の考え方ができる

特徴としてはこんなところでしょうか?
では実際にvectorを使ってみましょう。

// 「vector」の宣言
vector v;

// 要素の代入
for(int i=0; i != 10; i++) {
v.push_back(i);
}

// 要素数の出力
cout << v.size() << endl;

// 末尾への要素削除
v.pop_back();

// 末尾への要素追加(配列のMAXは気にする必要なし)
v.push_back(100);

// 要素参照
for(unsigned u=0; u != v.size(); ++u) {
cout << v[u] << ",";
}

上の機能をC言語で実現した場合の違いを書かなくては
何が凄いかは分かりづらいかもしれないですね。

まず、要素数を抑えるために別の変数を用意する必要があります。

int v[10];
unsigned size=0;

// 要素の代入
for(; size != 10; size++) {
v[size]=size;
}

// 要素数の出力
cout << size << endl;

// 末尾への要素削除
size--;

// 末尾への要素追加(配列のMAXは気にする必要なし)
size++;
if (size < 11) {
v[size]=100;
} else {
cout << "要素数オーバー" << endl;
return -1;
}

// 要素参照
for(unsigned u=0; u != size; ++u) {
cout << v[u] << ",";
}
return 0;
}

こんな感じになります。
あんまり凄さは分かんないですかね?

size++;
if (size < 11) {
v[size]=100;
} else {
cout << "要素数オーバー" << endl;
return -1;
}

いろんな意味で、この部分が必要なくなるって事が
もの凄い事なんですよね。

「vector」とは一つのオブジェクトですので
様々な情報も保持しています。

配列の情報として一番重要なのは要素数という事になります。
それも別の変数で管理する必要はなく
「v.size()」と記述する事で情報の取得が行えます。
ここがオブジェクトの素晴らしいところです。

さらに「vector」には数々のアルゴリズムが用意されています。
配列を扱う上でメンドクサイ作業の一つに
配列の途中に要素を挿入する、又は途中の要素を削除すると
いった作業がありました。
純粋にアルゴリズムと言われるプログラムを書いていたと思いますが
「vector」にはこの辺りも汎用プログラムとして既に存在します。

v.clear();
と記述するだけで、配列内の全ての要素が削除されます。
この作業を行う方法はいろいろとあったと思います。
外部で管理している要素数に「0」を代入する
といった作業と同じ感じです。

シーケンシャルなコンテナである配列への要素追加は
末尾に追加されるのが一般的です。
例にも出てきましたが

v.push_back(val);
は要素の追加を行います。

a[配列要素数のMAXを管理している変数]=val;
配列要素数のMAXを管理している変数++;

こんな感じの処理を行っています。

逆に要素を取り出しするのが
v.pop_back();

配列要素数のMAXを管理している変数--;
ってところでしょうか。

イテレータという考え方を覚えると、
このコンテナに実装されているアルゴリズムが
さらに沢山ある事が分かります。

次回はイテレータという考え方に触れて
この「vector」の続きを少し書きたいと思います。

スーパーボール

今年はNFLを結局1試合しか見ませんでした。
ニューイングランドの試合はまったく見なかったです。
サイトで結果のみ確認していましたが
負けない負けないの連続で気が付けばスーパーボールへ。

私の記憶ではこんなチームなったのですが
案の定その通りで、スーパーボールに勝利すると
1972以来の快挙となったようです。
1972といったら、私がこの世に生を受ける前ですから
もの凄い事だと感じます。

ニューイングランドが勝つものだと思っていたのですが
結果はまさかの敗退。

しかも残り1分を切ってからの逆転劇は
創られたドラマのような展開です。

勝利したのはニューヨークジャイアンツで偉大な兄、
ペイトン・マニングの弟であるイーライ・マニングが
スーパーボールMVPに選ばれました。

残念ながら感動の試合を生中継で見る事もできず
まだ録画したものも見ていない状況です。
今年は例年以上にNFLを見る事ができませんでした。

月曜日, 2月 04, 2008

奇跡が...

土曜日は、何気ない一言から奇跡が起きてしまいました。
週末は子供を何処かへ連れてって上げたいと思っているのですが
なかなか、この寒さでは良い場所もないのが現実です。
今週から東京競馬場で開催される事もあり
「競馬場に行こう」と言う事で生後2ヶ月の子供を連れて
競馬場に出かけました。
生後2ヶ月のデビューは次女を超える速さでのデビューです。
競馬場にはエアーで膨らんだ大きい遊ぶものがあり
長女はそれが大好きで、それをやらせるのが目的でした。

次女は去年の東京競馬の時にデビューさせたのですが
少し、怖がっていて今一だったようでしたが
今回は非常に面白そうに遊んでいました。

乗馬もさせた後に馬券予想です。
ここで奇跡が...

メインの東京新聞杯です。
私の馬券はまったく外れました。かすりもしません。
が、、、嫁の馬券が3連複でヒットしているではないですか!!
奇跡の50万馬券を的中です。
興奮が抑えられない、私と嫁はびっくりです。
もちろん、子供は何が起きているのかは理解していません。
夢のような出来事でした。

その足で嫁の実家に向かった私たちは、温泉に浸かり
有意義な一日を過ごす事ができました。

翌朝、外を見ると一面が銀世界となっていました。
そのため、帰りが大変な事になってしまいました。

子供達は雪を楽しんでいましたが、私達は子供3人を
引き連れているから一苦労です。

家に帰った後に、マンションの庭で
3歳と1歳の2人の娘と雪だるまを
家族分の5つ一緒に造ってしまいました。
ほとんど、自分一人で造りましたが...

それでもかなり喜んでいたので、それなりに満足した雪遊びでした。
こうやって奇跡のような週末は終わりました。

金曜日, 2月 01, 2008

魍魎の匣

この人の本は本当に面白い。
私の感想としては本編が面白いというよりは
本の中で語られる哲学的な事が面白い本ですね。

前回の「姑獲鳥の夏」の時は仮想現実の話でしたが
今回は宗教、超能力者、霊能者といったものに対する
考え方が沢山書かれていて、この部分にかなり共感を覚え、
また一つ考え方の幅が広がった気がします。

ストーリーは正直面白いとは言い難いというのが意見ですね。
話としては非常に重たいので映画化されるようですが、人気はどうでしょう。
ただ、最後に謎が一つ一つ明かされていくシーンは驚きの連続ですね
そもそも、謎を解くヒントが隠されていたのでしょうか?
まあ正直、分かりませんでした。

個人的には京極堂が話す「宗教、超能力者、霊能者」のところは
削ってほしくないです。
一番のこの作品に印象を受けた個所ですから。

魍魎という言葉を辞書で引いてから読むとまた面白いかもしれません。

兎に角長編です。読み始める前に人によっては心の準備もいるかもしれません。