2010年5月31日月曜日

ドア・イン・ザ・フェイス・テクニック

ドア・イン・ザ・フェイス・テクニックは罪悪感を利用して要求を呑ませる心理テクニックの一つです。

ドア・イン・ザ・フェイス・テクニックは本来の要求より高い要求をあえて行い、相手にわざと断らせて罪悪感を持たせます。そして、すかさず本来の要求を行うというテクニックです。

例えば、仕事の依頼を受ける金額を決めるときに最初はあえてふっかけた金額を提示します。相手が「ちょっとそれはないですよぉ」となったら「じゃぁ今回は特別に・・・」とか何とか言って本来の金額(やや色をつけてもよい)を提示します。

フット・イン・ザ・ドア・テクニック

フット・イン・ザ・ドア・テクニックは相手に要求を段階的に呑ませるための心理テクニックの一つです。

フット・イン・ザ・ドア・テクニックは依頼の難易度を最初は小さくして相手が依頼を受け入れたら徐々に依頼の難易度をを上げていくというテクニックです。

例えば誰かに一万円借りようとするとき、いきなり一万円を要求するのではなく、まず相手が貸してくれそうな3000円を要求して、相手が要求を受け入れたら何かしらの理由をつけて一万円を要求します。
相手はお金を貸すという要求をすでに呑んでいるので拒む自由が有る程度奪われてしまっているのです。

2010年5月29日土曜日

目線の向きで相手の心を読む

今回紹介するのはいわゆる読心術ってやつで心理学の一つです。

心理学の知識があると仕事場でもプラーベートでも心理戦で優位に立てる場合があるので知っていて損はないと思います。

目線の向きで相手の心を読むというのは、ある人が考えごとをしたり、何かを思い出だそうとしたりしている時に内容に応じて目線が変わってくるというものです。

左上
過去に見た光景、体験などを思い出そうとしている。

右上
今までにないこと、新しいことを想像している(嘘や作り話を考えている

左下
聴覚に関することをイメージしている(曲のタイトル、メロディを思い出している)

右下
身体の動き関することをイメージしている(スポーツ、運動などをしている自分を思い出している)

ちなみにこれは人によっては逆になる場合(左利き等)があるようです。

フロー状態について

何かに夢中になって気がついたら「あれ、もうこんな時間か」てなことが結構あったりするんですが、この夢中になってる状態のことをフロー状態とかゾーン状態とか言うそうです。

フロー状態はすごく集中力が高まっているので生産性がかなり高い状態にあります。
しかし、ちょっとした外部からの刺激でこのフロー状態は解けてしまい再びフロー状態になるには15分以上の時間が必要なのだそうです。

仕事中に質問したりすると質問された人がフロー状態にいた場合、フロー状態が解けて生産性が下がってしまい更にフロー状態に再び入る為に15分以上の時間を無駄にしていまいます。

だから、うかつに質問したりしない方がいいんです。

私自身このフロー状態って、その通りだなと思っていて聞いた方が早いなと思っても極力しないで自分で調べるようにしています。

こんな話を仕事場でしたら、なんか鼻で笑われてちょっと悔しかったです。

2010年5月28日金曜日

集約とコンポジションの違い

今日は集約コンポジションの違いについて書きます。
集約もコンポジションもUMLのクラス図で使われる言葉で、両方ともHAS-Aの関係を表します。記号で書くと
  • ◆-がコンポジション
  • ◇-が集約
です。HAS-Aの関係とはどういうものかというと、簡単に言えば「ClassA HAS A ClassB」をクラス図で表現した関係です。具体的に言うとClassAのメンバー変数としてClassB型の変数が宣言されている状態のことです。
さて、本題です。
  • 集約はClassA型の変数が消滅してもClassB型のメンバー変数は消滅しない
  • コンポジションはClassA型の変数が消滅したらClassB型のメンバー変数も消滅する
これが集約とコンポジションの違いで、コンポジションは運命共同体という言い方が出来ます。
class ClassA {
    // field
    ClassB b;
};
これはコンポジションです。

class ClassA {
 
    // field
     ClassB* b;

    // destructor
    ~ClassA() {
        delete b;
    }
};
これもコンポジションです。

class ClassA {
 
    // field
     ClassB* b;

    // destructor
    ~ClassA() {
        // do nothing...
    }
};
これは集約です。

2010年5月26日水曜日

C++でTemplateMethodパターン

TemplateMethodパターンはスーパークラス(ベースクラス)でロジックの概要設計あるいは共通部分を実装し、詳細ロジックあるいは非共通部分をサブクラスで提供するパターンです。
大げさに言うとスーパークラスでプチフレームワークを実装するようなイメージです。
TemplateMethodパターンのメリットは
  • サブクラスで共通なロジックをスーパークラスで共通化できる。
  • ロジックの骨組みが明確になっている為、コードが把握し安い且つメンテナンスコストが軽減されやすい。
などです。
それではクラス図をご覧ください。
ちょっとメソッドとか多いですけど簡単な継承関係です。
このクラス図で重要なのは
  • move()
  • canMove()
  • canMoveEach()
  • moveEach()
で、Characterクラスのmove()がテンプレートメソッドに相当します。
次にソースを見てみましょう、用意するのは次のファイルです今回からクラスのcppファイルを用意しない方が見やすいかなと思って.hだけ用意しました。
  • main.cpp
  • Character.h
  • Monster.h
Character.h
#ifndef _CHARACTER_H_
#define _CHARACTER_H_

#include 

class Character
{
    //----------------------------------
    //  field
    //----------------------------------
private:
    int HP;
    int MP;

    //----------------------------------
    //  property
    //----------------------------------
public:
    const int getHP() {
        return this->HP;
    }
    Character& setHP(const int _hp) {
        this->HP = _hp;
        return *this;
    }
    const int getMP() {
        return this->MP;
    }
    Character& setMP(const int _mp) {
        this->MP = _mp;
        return *this;
    }

    //----------------------------------
    //  method
    //----------------------------------
public:
    Character() :
        HP(100),
        MP(100)
    {
        //  do nothing...
    }

    virtual ~Character() {
        //  do nothing...
    }

    void move() {   //  テンプレートメソッド
        //  move可能判定
        if (this->canMove()) {
            //  move
            this->moveEach();
        }
    }
private:
    virtual const bool canMove() {
        //  HPが0ならmove不可
        if (this->getHP() <= 0) {
            return false;
        }
        return this->canMoveEach();
    }

protected:
    virtual const bool canMoveEach() = 0;
    virtual void moveEach() = 0;
};
#endif
move()のなかでmove可能ならばmoveするという大まかなロジックを実装しています。

Monster.h
#ifndef _MONSTER_H_
#define _MONSTER_H_

#include "Character.h"

class Monster : public Character
{
 //----------------------------------
 // method
 //----------------------------------
protected:
 virtual ~Monster() {
  // do nothing...
 }
 virtual const bool canMoveEach() {
  printf("canMoveEach\n");
  return true;
 }

 virtual void moveEach() {
  printf("moveEach\n");
 }
};
#endif
Monster独自のcanMove()、move()をcanMoveEach()、moveEach()で実装しています。Eachっていう名前の付け方は一般的ではない気がします。

main.cpp
#include 
#include "Monster.h"

void main(void)
{
    //  Monster生成
    Character* character = new Monster();

    //  move
    character->move();

    //  HPに0を設定
    character->setHP(0);

    //  move
    character->move();

    delete character;
    printf("hit any key...\n");
    while(getchar() == EOF) {}
}

引数の命名規則はアンダーバー「_」+キャメルケース

今更ですけど、メソッド(関数)の引数の命名規則はアンダーバー「_」+キャメルケースを使用しています。
キャメルケースというのは各単語や要素語の先頭の文字を大文字で表記する手法のことです。
例)camelCase、guitarSpec...
そして引数の先頭にアンダーバー「_」を付与しています。アンダーバーを付ける利点は
  • 引数であることが一目でわかる。
  • クラスのメンバ変数やメソッド内の変数と名前がかぶることが無くなるため名前を考えるのに困らない。
です。
例)_camelCase、_guitarSpec...

2010年5月25日火曜日

C++でクラスメンバ変数を参照型にする

今日はC++でクラスのメンバ変数参照型にする方法を紹介します。
その方法とは?!
  • コンストラクタ初期化子を使用する
です。
コンストラクタ初期化子を使うとクラスメンバの生成と同時に値を代入したり、メンバのコンストラクタに引数を与えることができます。
これを利用して参照型の変数を初期化することが可能です。
サンプルコードを用意しました。
#include 
#include "Monster.h"

void main(void)
{
    int hp = 111;
    int mp = 222;

    //  Monster生成
    Monster monster(hp, mp);

    //  参照している変数の値を変更
    hp = 100;
    mp = 100;

    //  表示
    printf("HP:%d\n", monster.HP);
    printf("MP:%d\n", monster.MP);

    printf("hit any key...\n");
    while(getchar() == EOF){}
}
#ifndef _MONSTER_H_
#define _MONSTER_H_

#include 

class Monster
{
    //----------------------------------
    //  field
    //----------------------------------
public:
    int& HP;    //  メンバ変数が参照型
    int& MP;    //  メンバ変数が参照型
    //----------------------------------
    //  method
    //----------------------------------
public:
    //  constructor
    Monster(int& _hp, int& _mp) :   //  コロンをわすれずに!
        HP(_hp),    //  初期化子
        MP(_mp)     //  初期化子
    {
        //  do nothing...
    }

};
#endif
ソースコードの内容については余りつっこまないでください(汗
時間がなくてこんな例しか作れませんでした。

2010年5月24日月曜日

C++のエラー:error C2143: 構文エラー : ';' が 'namespace' の前にありません。

今日出たエラーです。
エラーの原因はクラス宣言の括弧の後ろにセミコロン「;」が抜けているだけだったのですが、エラーが出てる場所は全然違う場所で結構迷走しました。
#ifndef _MONSTER_H_
#define _MONSTER_H_

#include "Character.h"

class Monster : public Character
{
    //----------------------------------
    //  method
    //----------------------------------
    virtual const bool canMoveEach() {
        return true;
    }

    virtual void moveEach() {
    }
}//←ココ
#endif

2010年5月23日日曜日

C++でオブジェクトのクラス名(型名)文字列を実行時に取得する

今日はC++オブジェクトクラス名を実行時に取得する方法を紹介します。
紹介するといってもtypeid()という関数を使用するだけです。typeid()はtypeinfo.hで定義されているのでincludeする必要があります。
typeid()の引数には変数オブジェクトや型名(クラス名)を直接指定することが出来ます。
またtypeid()の戻り値同士を比較したりも出来ます。

サンプルコードを用意しました。

main.cpp
#include 
#include 
#include "ConcreteClass.h"

void main(void)
{
    //  ConcreteClassオブジェクト生成
    AbstractClass* obj = new ConcreteClass();

    //  型名取得
    const type_info& id = typeid(*obj);
    printf("%s\n", id.name());

    //  method()の呼び出し
    obj->method();

    //  型を直接指定し型名を取得し比較
    if (typeid(ConcreteClass) == id) {
        printf("true\n");
    }

    delete obj;

    printf("hit any key...\n");
    while(getchar() == EOF) {}
}

AbstractClass.h
#ifndef _ABSTRACT_CLASS_H_
#define _ABSTRACT_CLASS_H_

#include 
#include 

class AbstractClass
{
    //----------------------------------
    //  method
    //----------------------------------
public:
    void method() {
        //  型情報の取得
        const type_info& id = typeid(*this);

        //  表示
        printf("AbstractClass-typeid:%s\n", id.name());

        //  subMethod()の呼び出し
        this->subMethod();
    }

protected:
    virtual void subMethod() = 0;
};
#endif

ConcreteClass.h
#ifndef _CONCRETE_CLASS_H_
#define _CONCRETE_CLASS_H_

#include 
#include 
#include "AbstractClass.h"

class ConcreteClass : public AbstractClass
{
 //----------------------------------
 // method
 //----------------------------------
public:
 ConcreteClass() {
  // do nothing...
 }

 virtual ~ConcreteClass() {
  // do nothing...
 }

protected:
 virtual void subMethod() {
  // 型情報の取得
  const type_info& id = typeid(*this);

  // 表示
  printf("ConcreteClass-type_id:%s\n", id.name());
 }

};
#endif

2010年5月21日金曜日

IE(Internet Explorer)でキャッシュを無視して更新する

ブラウザ(IE、Firefoxで確認)Ctrl+F5を押下するとキャッシュ内容を無視して 更新してくれるということを今日知りました。
今まではURLの最後に使用しないURLパラメータを毎回つけたりしていたのですがそんな手間も今日でおさらばです、やったー♪

サーバサイドのプログラムを作成して、ページを更新しても修正内容が更新されないってのは良くある話ですよね。これはブラウザがすでに読み込んだページ内容を保存(キャッシュ)していてそれを表示しているからで、そのことによって次のようなメリットがあります。
  • サーバーからページ内容を読み込むよりも速く内容を表示できる。
  • サーバーからの読み込みないためネットワークの負荷を軽減できる。
なのでブラウザが勝手にやってくれているのですが、がしかしサーバー側のプログラムが修正されていてもそんなことは気にしてはくれないのです(T△T)。
それを回避するためにURLパラメータを変更してキャッシュ情報とは違いますよとブラウザにウソついてページを更新してもらっていました。
URLパラメータってのは
  • http://www.blogger.com/post-edit.g?blogID=1053615194817097162
「blogID=1053615194817097162」のことです。

WSHで(JScript)IEを操作する

WSHIE(InternetExplorer)操作できるようなので簡単な検索操作をスクリプトにやらせてみました。
メソッドやプロパティ等はMSDNのリファレンスを参考にしました。
//  IEオブジェクト生成&IE機動
    var objIE = WScript.CreateObject("InternetExplorer.Application", "IE_");
    //  可視状態にする
    objIE.Visible = true;
    //  検索ページの表示
    objIE.GoSearch();
    //  シェルオブジェクト生成
    var objShell = WScript.CreateObject("WScript.Shell");
    //  ページが表示されるまで待つ
    WScript.Sleep(5000);
    //  キー送信
    objShell.Sendkeys("Google");
    //  Enterキー送信
    objShell.Sendkeys("{ENTER}");

//function IE_DownloadComplete() {
//  objShell.Sendkeys("Google");
//  objShell.Sendkeys("{ENTER}");
//}
検索ページが読み込まれたことをイベントで受け取ろうとしたのですがうまくできませんでした、JScriptだとつかえないイベントが結構あるようです。
VBAを使いなさいってことなのかな。。。

2010年5月20日木曜日

WSH(JScript)におけるカレントディレクトリについて、その2

先日スクリプトファイルを直接実行した時とファイルやフォルダをドラッグアンドドロップした時とでは、カレントディレクトリが変わるっていう記事を書きましたが、WScript.ScriptFullNameを使用すればスクリプトファイルのあるフォルダを取得できますね。
まぁ、カレントディレクトリ==スクリプトが置いてあるフォルダとか思っていた私がウマシカさんでしたw
スクリプトフォルダを取得する関数を作ってみました。
WScript.echo(this.getScriptFolder());

//  スクリプトフォルダの取得
function getScriptFolder() {
    return WScript.ScriptFullName.replace("\\" + WScript.ScriptName, "");
}

2010年5月18日火曜日

WSH(JScript)でフォルダ配下の全ての動画ファイルの再生時間を取得する

最近WSHの記事ばっかりでしたけどこんなの作ってました。
主な機能
  1. 動画ファイルの再生時間を取得する。
  2. サブフォルダも含めたフォルダ内の全ての動画ファイルの再生時間を取得する。
  3. logファイルに出力する。
try {
    var main = new main(WScript.Arguments);
    main.exec();
}
catch(e) {
    WScript.echo(e);
    WScript.Quit();
}

function main (_args) {

    // Windows Media Playerコントロールオブジェクト生成
    this.objWMP = WScript.createObject("WMPlayer.OCX");
    //  FileSystemObject生成
    this.objFileSys = WScript.CreateObject("Scripting.FileSystemObject");
    //  ShellObject生成
    this.objShell = WScript.createObject("WScript.Shell");   

    //  引数の取得
    this.objArgs = _args;
    if (this.objArgs.UnNamed.count <= 0) {
        throw "ファイル叉はフォルダをドラッグ&ドロップするか\nコマンドラインから引数としてファイル又はフォルダパスを指定してください。";
    }

    //---------------------------------
    //  exec()
    //---------------------------------
    this.exec = function() {
        //  logファイルの生成(上書きモード)
        var logFileName = "PlayTimeLog.txt";
        var objLogFile = this.objFileSys.OpenTextFile(logFileName, 2, true);
        var fileCnt = 0;
        //  全ての名前なし引数について処理する
        var unNamedArgs = this.objArgs.UnNamed;
        var enu = new Enumerator(unNamedArgs);
        for (; enu.atEnd() == false; enu.moveNext()) {
            try {
                var arg = enu.item();
                fileCnt = this.logPlayTime(objLogFile, arg);
            }
            catch(e) {
                objLogFile.WriteLine(e);
            }
        }
        //  サマリー情報の書き込み
        objLogFile.WriteLine("----------------------------------------");
        objLogFile.WriteLine(fileCnt + "件のファイルを処理しました。");
        //  logファイルのクローズ
        objLogFile.Close();
        //  完了メッセーの表示
        WScript.echo(
            "mission completed\n"
            + "作業結果は\n"
            + this.objShell.CurrentDirectory
            + "\\" + logFileName + "\n"
            + "参照"
        );
    }

    //---------------------------------
    //  サブフォルダも含めて動画ファイルの再生時間をlogファイルに書き込む
    //---------------------------------
    this.logPlayTime = function (_objLogFile, _path) {
        var fileCnt = 0;
        //  パスがフォルダの場合
        if (this.objFileSys.FolderExists(_path)) {
            //  フォルダオブジェクトの生成
            var objFolder = this.objFileSys.GetFolder(_path);
            //  サブフォルダ一覧取得
            var subFolders = objFolder.SubFolders;
            //  全てのフォルダについて自分を呼び出す
            var enuSubFolders = new Enumerator(subFolders);
            for (;enuSubFolders.atEnd() == false; enuSubFolders.moveNext()) {
                fileCnt += this.logPlayTime(_objLogFile, enuSubFolders.item().Path);
            }

            //  ファイル一覧取得
            var files = objFolder.Files
            //  全てのファイルについて自分を呼び出す
            var enuFiles = new Enumerator(files);
            for (; enuFiles.atEnd() == false; enuFiles.moveNext()) {
                fileCnt += this.logPlayTime(_objLogFile, enuFiles.item().Path);
            }
        }
        //  パスがファイルの場合
        else if (this.objFileSys.FileExists(_path)) {
            //  ファイルオブジェクトの生成
            var objFile = this.objFileSys.GetFile(_path);
            //  再生時間の取得
            var time = this.getPlayTime(objFile.Path);
            //  logファイルに書き込み
            _objLogFile.WriteLine(objFile.Name + "\t" + time);
            //  ファイル数をインクリメント
            fileCnt++;
        }
        else {
            objLogFile.WriteLine("error:" + _path + "は存在しません");
        }
        return fileCnt;
    }

    //---------------------------------
    //  動画再生時間の取得
    //---------------------------------
    this.getPlayTime = function(_filePath) {
        // 動画ファイルの読み込み
        this.objWMP.URL = _filePath; 

        // ファイル準備中状態の間待つ
        while (this.objWMP.playState == 9) {
            WScript.Sleep(500);
        }

        // 再生時間取得
        return this.objWMP.currentMedia.durationString;
    }

}
ソースコード長いw

2010年5月17日月曜日

WSHにおけるカレントディレクトリについて

WSHのスクリプトファイルを直接実行した時とファイルやフォルダをドラッグアンドドロップした時とでは、カレントディレクトリが変わるようです。
ファイルやフォルダをドラッグアンドドロップしたときはカレントディレクトリが「Documents and Settings」配下のログインユーザディレクトリ配下になります。
下記に検証用のスクリプトを用意しました。
var shell = WScript.createObject("WScript.Shell");
WScript.echo(shell.CurrentDirectory);

WSH(JScript)でDictionaryを使ってみる

今日はWSH(JScript)のDictionaryを紹介します。
var datas = {
    a:"aa",
    b:"bb",
    c:"cc"
};

//  辞書(Dictionary)の生成
var objDict = WScript.createObject("Scripting.Dictionary");
for (var key in datas) {
    //  要素の追加
    objDict.add(key, datas[key]);
}

//--------------------------------------
//  プロパティ
//--------------------------------------
//  要素数の取得
var cnt = objDict.Count;
WScript.echo("要素数の取得:" + cnt);

//  要素の取得
WScript.echo("要素の取得1:" + objDict.Item("a"));
WScript.echo("要素の取得2:" + objDict("a"));

//  キーを変更する
objDict.Key("a") = "A";
WScript.echo("\"A\"の値:" + objDict.Item("A"));

//--------------------------------------
//  メソッド
//--------------------------------------

//  キーの存在判定
if (objDict.Exists("A")) {
    WScript.echo("\"A\"は存在します");
}

//  Enumeratorを使用した反復
var enuDict = new Enumerator(objDict);
var strItems = "";
for (; enuDict.atEnd() == false; enuDict.moveNext()) {
    strItems += objDict(enuDict.item()) + ",";
}
WScript.echo(strItems);



//  キーを指定して削除
objDict.Remove("b");
objDict.RemoveAll();
WScript.echo("要素数:" + objDict.Count);
紹介しておいてなんですが、機能が微妙な上にJScript(JavaScript)は連装配列が使えるのでDictionaryは使わないかもしれません。

2010年5月16日日曜日

JavaScript(JScript)で連装配列を反復する(なめる)

今日はJavaScript(JScript)で連装配列を反復する方法を紹介します。
var array = new Array();

array["a"] = "aa";
array["b"] = "bb";

for (var key in array) {
    //  WScript.echo(array[key]);   // WSHの場合
    alert(array[key]);
}

2010年5月15日土曜日

WSH(JScript)で動画の再生時間を取得する

今日はWSH(JScript)で動画再生時間取得する方法を紹介します。
try {
    // Windows Media Playerコントロールオブジェクト生成
    var objWMP = WScript.createObject("WMPlayer.OCX");

    // 動画ファイルの読み込み
    objWMP.URL = ".\\aaa.wmv"; 

    // ファイル準備中状態の間待つ
    while (objWMP.playState == 9) {
        WScript.Sleep(500);
    }

    // 再生時間取得と表示
    WScript.echo(objWMP.currentMedia.durationString);
}
catch(e) {
    WScript.echo(e);
    WScript.Quit();
}
「aaa.wmv」というファイルの再生時間を表示しています。

2010年5月14日金曜日

WSH(JScript)でコレクションを反復する

今日はWSH(JScript)コレクション反復する方法を紹介します。
今回 紹介する方法は
  • Enumratorを使用して反復する
です。
例として指定フォルダのファイル一覧を表示するコードを記述します。
try {
    //  FileSystemObject生成
    var objFileSys = WScript.CreateObject("Scripting.FileSystemObject");

    //  ファイル一覧(コレクション)取得
    var folder = objFileSys.GetFolder("C:");
    var files = folder.Files;

    //  Enumeratorを使用してファイル一覧を反復する
    var str = "";
    var iter = new Enumerator(files);
    for (;iter.atEnd() == false; iter.moveNext()) {
        str += iter.item().Name + "\n";
    }

    //  ファイル一覧表示
    WScript.echo(str);
}
catch (e) {
    WScript.echo(e);
    WScript.quit();
}

2010年5月13日木曜日

構造体(struct)の宣言方法にかんする小技

今日はC、C++で構造体を宣言する際の小技を紹介します。小技にもほどがあるって感じなんですけど変数を定義する際にstructっていちいちつけなくても良くなる方法です。
下記がコードです。
#include 

struct Character {
    char name[32];
    int HP;
    int MP;
};

typedef struct {
    char name[32];
    int attack;
}Weapon;

void main (void)
{
    struct Character character; //  struct が必要
    Weapon knife;   //  struct が不要

    printf("hit any key...");
    while(getchar() == EOF){}

}
typedefを使用してデータ型に新しい名前を付けることが出来るのですが、Weaponは宣言を直接typedefしています。

2010年5月12日水曜日

とりあえずconst!

ってぐらい変数にはconst付けてます。ん?だめ?
理由としては
  • コンパイラにバグを見つけさせる為
です。constが付いているとその変数は読み取り専用となる為、誤って値を代入したり、オブジェクトの内容が変わるようなメソッドを呼び出したり出来なくなります。
具体的に説明すると、
const int value = 1;
    if(value = 1) // ← ==のつもり
こんなのとか
const CHoge hoge;
    hoge.clear(); // ←本当はclearしてはダメ
とかコンパイルエラーになります。
C#でも使えたらいいのにって思うのは私だけ?

2010年5月10日月曜日

C,C++で配列を反復する(なめる)

今日はC,C++で配列を反復する方法を2つ紹介します。
  • 配列の要素数を使用して反復する。
  • 番兵を使用して反復する。
そんなに難しくないのでコードをサクッと記述します。
#include 

void main(void)
{

    //----------------------------------
    // サイズを求めて反復に使用する
    //----------------------------------
    const char* strArray[] = {
        "1",
        "2",
        "3",
    };
    const int length = sizeof(strArray) / sizeof(strArray[0]);    // 配列のサイズ算出
    printf("length:%d\n", length);
    for (int i = 0; i < length; i++) {  // length未満の間ループ
        printf("%s\n", strArray[i]);
    }

    printf("--------\n");

    //----------------------------------
    // 番兵を立てる
    //----------------------------------
    const char* strArray2[] = {
        "a",
        "b",
        NULL,   // 番兵
    };

    for (int i = 0; strArray2[i]; i++) {    // false(NULL)以外の間ループ
        printf("%s\n", strArray2[i]);
    }

    printf("hit any key...\n");
    while(getchar() == EOF){}
}

2010年5月9日日曜日

コンストラクタで仮想関数を呼び出す(C++)

C++ではコンストラクタで仮想関数を呼び出しても意図した動作にならないんですよね、どんな動きになるかといいますと、オーバーライドした関数ではなく基底クラス(親クラス)の仮想関数が呼び出されてしまいます。
検証用のコードを用意しましたのでまずはクラス図を見てみましょう。

単純な継承関係のクラスを二つ用意しました。init()は仮想関数になっています。
つぎにソースを見てみましょう、用意したのは次のファイルです。
  • main.cpp
  • Character.h
  • CharacterMonster.h
まずはCharacter.hです。
#ifndef _CHARACTER_H_
#define _CHARACTER_H_

#include 
class Character
{
public:
    // constructor
    Character() {
        this->init();
    }

    // destructor
    ~Character() {
        // do nothing...
    }

protected:
    virtual void init() {   //  仮想関数で定義
        printf("init Character\n");
    }
};
#endif

次にCharacterMonster.hです。
#ifndef _CHARACTER_MONSTER_H_
#define _CHARACTER_MONSTER_H_

#include "Character.h"

class CharacterMonster : public Character
{
public:
 // constructor
 CharacterMonster() {
  // do nothing...
 }

 // destructor
 virtual ~CharacterMonster() {
  // do nothing...
 }

protected:
 virtual void init() { // オーバーライド
  printf("init Character Monster\n");
 }
};
#endif

最後にmain.cppです。
#include "CharacterMonster.h"
#include 

void main(void)
{
    Character* character = new CharacterMonster();
    delete character;

    printf("hit any key...");
    while(getchar() == EOF){}
}
init()はオーバーライドされているのでこのプログラムを実行すると「init Character Monster」が表示されると思いきや「init Character」が表示されてしまいます。
ということでコンストラクタで仮想関数を呼び出しても意図した動作になりませんでした。
ちなみにinit()を純粋仮想関数にした場合は実体がないためリンクエラーとなります。

なのでinit()メソッドをいつも用意するようにしているのですけどinit()を呼び出すのを忘れたりもするんですよね、ケースバイケースなのかなぁ?。

C++でStrategyパターン

Strategyパターンはアルゴリズム(振る舞い)をカプセル化(クラス化)するパターンです。
Singletonパターンのメリットは
  • 振る舞いを簡単に交換することが出来る。
  • 振る舞いを動的に交換することが出来る。
です。
まずは次のクラス図をご覧ください

見ました?なんか複雑だからってあんまり見てないでしょ?
ちゃんとクリックして拡大して見てくださいね。
このクラス図で重要なのは
  • MonsterStrategyクラス
ですMonsterのmove()の振る舞いをMonsterStrategyクラスに委譲しています。
つぎにソースを見てみましょう、用意するのは次のファイルです。

  • main.cpp
  • Monster.h
  • Monster.cpp
  • MonsterStrategy.h
  • MonsterStrategy.cpp
  • MonsterStrategyFreedom.h
  • MonsterStrategyFreedom.cpp
  • MonsterStrategyGangan.h
  • MonsterStrategyGangan.cpp
まずはMonster.hです。
#ifndef _MONSTER_H_
#define _MONSTER_H_

#include "MonsterStrategy.h"

class Monster
{
    //----------------------------------
    // constant
    //----------------------------------
    #define STRATEGY_MAX_NUM 8

    //----------------------------------
    // field
    //----------------------------------
private:
    MonsterStrategy* m_strategyDictionary[STRATEGY_MAX_NUM];    //  ①
    MonsterStrategy::Type m_currentStrategyType;                //  ②

    //----------------------------------
    // property
    //----------------------------------
public:
    const MonsterStrategy::Type getCurrentStrategyType();
    Monster& setCurrentStrategyType(const MonsterStrategy::Type _type);

private:
    MonsterStrategy** getStrategyDictionary();
    const bool addStrategy(const MonsterStrategy::Type _type);

    //----------------------------------
    // mothod
    //----------------------------------
public:
    // constructor
    Monster();

    // destructor
    ~Monster();

    // initialize
    const bool init();

    // move
    Monster& move();    //  ③

private:
    // operator(=)
    Monster& operator=(const Monster& _monster);
};
#endif
①Monsterの行動アルゴリズム辞書です。今回は配列を使用していますがDictionary系クラスを私用するのが良いでしょう。
②現在の行動アルゴリズムです。
③Monsterに行動指示を出します。

次にMonster.cppです。
#include "Monster.h"
#include "MonsterStrategyFreedom.h"
#include "MonsterStrategyGangan.h"
#include 

//-----------------------------------
// property
//-----------------------------------
const MonsterStrategy::Type Monster::getCurrentStrategyType()
{
    return this->m_currentStrategyType;
}
Monster& Monster::setCurrentStrategyType(const MonsterStrategy::Type _type)
{
    printf("strategy %d -> %d\n", this->getCurrentStrategyType(), _type);
    this->m_currentStrategyType = _type;
    return *this;
}
MonsterStrategy** Monster::getStrategyDictionary()
{
    return this->m_strategyDictionary;
}
const bool Monster::addStrategy(const MonsterStrategy::Type _type)
{
    if (STRATEGY_MAX_NUM <= _type) {
        throw "error strategy max num";
    }
    MonsterStrategy** strategyDictionary = this->getStrategyDictionary();
    if (strategyDictionary[_type] != NULL) {
        // already exist
        return false;
    }

    switch (_type) {
        case MonsterStrategy::ST_FREEDOM:
            strategyDictionary[_type] = new MonsterStrategyFreedom();
            break;
        case MonsterStrategy::ST_GANGAN:
            strategyDictionary[_type] = new MonsterStrategyGangan();
            break;
        default:
            throw "invalid type";
            break;
    }

    return true;
}

//-----------------------------------
// method
//-----------------------------------
// constructor
Monster::Monster() :
    m_currentStrategyType(MonsterStrategy::ST_FREEDOM)
{
    // do nothing...
}
// destructor
Monster::~Monster()
{
    MonsterStrategy** strategyDictionary = this->getStrategyDictionary();
    for (int i = 0; i < STRATEGY_MAX_NUM; i++) {
        delete strategyDictionary[i];
    }

}

// initialize
const bool Monster::init()
{
    MonsterStrategy** strategyDictionary = this->getStrategyDictionary();
    for (int i = 0; i < STRATEGY_MAX_NUM; i++) {
        strategyDictionary[i] = NULL;
    }

    const MonsterStrategy::Type strategyTypeArray[] = {
        MonsterStrategy::ST_FREEDOM,
        MonsterStrategy::ST_GANGAN,
    };
    const int size = sizeof(strategyTypeArray) / sizeof(MonsterStrategy::Type); //  ①
    for (int i = 0; i < size; i++) {
        this->addStrategy(strategyTypeArray[i]);
    }

    return true;
}

// move
Monster& Monster::move()
{
    MonsterStrategy** strategyDictionary = this->getStrategyDictionary();
    strategyDictionary[this->getCurrentStrategyType()]->move(*this);
    return *this;
}
①配列のサイズを求めています。

次にMonsterStrategy.hです。
#ifndef _MONSTER_STRATEGY_H_
#define _MONSTER_STRATEGY_H_

class Monster;

class MonsterStrategy
{
public:
    //----------------------------------
    // constant
    //----------------------------------
    enum Type {
        ST_FREEDOM = 0, // 自由
        ST_GANGAN,  // がんがん行こうぜ
    };  //  ①

    //----------------------------------
    // mothod
    //----------------------------------
public:
    // constructor
    MonsterStrategy();

    // destructor
    virtual ~MonsterStrategy();

    // move
    virtual void move(Monster& _monster) = 0;   //  ②

private:
    // operator(=)
    MonsterStrategy& operator=(const MonsterStrategy& _monsterStrategy);

};
#endif
①Monsterの行動アルゴリズムのタイプです。
②Monsterの行動アルゴリズムが記述されているメソッドです。タイプ毎にアルゴリズムは違うので純粋仮想関数にしています。

次にMonsterStrategy.cppです。
#include "MonsterStrategy.h"
#include 

//----------------------------------
// mothod
//----------------------------------
// constructor
MonsterStrategy::MonsterStrategy()
{
    // do nothing...
}

// destructor
MonsterStrategy::~MonsterStrategy()
{
    // do nothing...
}
特に何も実装はなしです。
次に
MonsterStrategyFreedom.h
MonsterStrategyFreedom.cpp
MonsterStrategyGangan.h
MonsterStrategyGangan.cpp
をまとめて記述します。
#ifndef _MONSTER_STRATEGY_FREEDOM_H_
#define _MONSTER_STRATEGY_FREEDOM_H_

#include "MonsterStrategy.h"

class Monster;

class MonsterStrategyFreedom : public MonsterStrategy // ①
{
public:

    //----------------------------------
    // mothod
    //----------------------------------
public:
    // constructor
    MonsterStrategyFreedom();

    // destructor
    ~MonsterStrategyFreedom();

    // move
    void move(Monster& _monster);   //  ②

private:
    // operator(=)
    MonsterStrategyFreedom& operator=(const MonsterStrategyFreedom& _monsterStrategyFreedom);

};
#endif
#include "MonsterStrategyFreedom.h"
#include "MonsterStrategy.h"
#include "Monster.h"
#include 

//----------------------------------
// mothod
//----------------------------------
// constructor
MonsterStrategyFreedom::MonsterStrategyFreedom()
{
    // do nothing...
}

// destructor
MonsterStrategyFreedom::~MonsterStrategyFreedom()
{
    // do nothing...
}

// move
void MonsterStrategyFreedom::move(Monster& _monster)
{
    printf("freedom\n");
}
#ifndef _MONSTER_STRATEGY_GANGAN_H_
#define _MONSTER_STRATEGY_GANGAN_H_

#include "MonsterStrategy.h"

class Monster;

class MonsterStrategyGangan : public MonsterStrategy    //  ①
{
public:

    //----------------------------------
    // mothod
    //----------------------------------
public:
    // constructor
    MonsterStrategyGangan();

    // destructor
    ~MonsterStrategyGangan();

    // move
    void move(Monster& _monster);   //  ②

private:
    // operator(=)
    MonsterStrategyGangan& operator=(const MonsterStrategyGangan& _monsterStrategyGangan);

};
#endif
#include "MonsterStrategyGangan.h"
#include "MonsterStrategy.h"
#include "Monster.h"
#include 

//----------------------------------
// mothod
//----------------------------------
// constructor
MonsterStrategyGangan::MonsterStrategyGangan()
{
    // do nothing...
}

// destructor
MonsterStrategyGangan::~MonsterStrategyGangan()
{
    // do nothing...
}

// move
void MonsterStrategyGangan::move(Monster& _monster)
{
    printf("inochi\n");
}
①MonsterStrategyを継承しています。
②オーバーライドしたmove()メソッドです。

最後にmain.cppです。
#include "Monster.h"
#include 

void main(void)
{
    Monster monster;    //  モンスター生成
    monster.init();     //  初期化
    monster.move();     //  move
    monster.setCurrentStrategyType(MonsterStrategy::ST_GANGAN); //  戦略変更
    monster.move();     //  move

    printf("hit any key...");
    while(getchar() == EOF){}
}

以上でStrategyパターンの説明は終わりです。クラス数は少し多いですがそんなに難しいパターンではないはずです。
ソースコードはコピペで動かせるハズですので是非試してみてください。

2010年5月8日土曜日

C++のエラー:error C2360: 'hoge' の初期化が 'case' ラベルによって行われませんでした

今日出たエラーなんですけどswitch文のcase内で変数定義と初期化を同時に行おうとしたらでました。
コードは
switch (_type) {
    case 1:
        const int hoge = 1; // これがダメらしい
         break;
}
こんな感じです。昔同じエラーを経験したことがあったのですぐ解決しました。
解決方法はcaseに{}をつけるだけです。
switch (_type) {
    case 1: { // ←ココ
        const int hoge = 1; 
         break;
    } // ←ココ
}
修正するとこんなかんじです。
どうしてダメなのかはよく分りません、C言語の仕様なのかな?

2010年5月6日木曜日

テストケースの上げ方について

今日はテストケースのデータを上げる際に気をつけていることを簡単に書きます。

1.数値のゼロ、文字のブランク
  これらの値はプログラマが考慮していない場合が多いのでケースに上げています。

2.境界値
 有効な範囲の境界となる値です。例えば3~6が有効な値だった場合、
  • 正常ケースで3と6
  • 異常ケースで2と7
 を上げるようにしています。

3.意地悪な値
 「1.」と同じような考え方ですが、何とかしてバグを見つけてやる、こんな値は考慮してないだろう、どうだー!?っていう値を考えますw。
 
以上です。(たった3つかよ!何か忘れている気がしなくもないですが・・・
箇条書きにするとたった3つですが、この3つが頭に入っていれば十分な品質が確保出来るんじゃないでしょうか?

2010年5月5日水曜日

C++のエラー:error C2065: 'NULL' : 定義されていない識別子です。

今日出たエラーです。
えっ?!って感じだったんですけど。
stdio.hに定義されていると言うことに今日気づきました。(汗

2010年5月4日火曜日

JavaScriptで「問い合わせ内容を入力してください。」を実装する

タイトルだけだよよく分らないかもしれませんが、
  1. テキスト入力エリアにあらかじめ「問い合わせ内容を入力してください」の文言を表示しておく。
  2. テキスト入力エリアにフォーカスがあたる。
  3. 「問い合わせ内容を入力してください」の文言が消える。
これの実装方法を紹介したいと思います。
まずはJavaScriptのコードです。

つづいてHTMLのコードです。

下のテキストボックスにフォーカスがあたると「問い合わせ内容を入力してください」が消えます。


C++のエラー:C1083: include ファイルを開けません。

今日出たエラーです。結構はまってしまいました。
最初はincludeパスが未設定なのかなっておもったんですけど、ちがってまして、main.cppからこつこつ作って行くハメに。。。
で結局なにが問題だったのかといいますと。。。Monster.hがMonster..hになっていました。
こう書いてもわかりづらい!よーくみてください。ファイル拡張子の前のドットが2個有ったw

2010年5月2日日曜日

C++のエラー:invalid use of 'this' in non-member function

今日出たエラーです。
「メンバー関数じゃないからthisは使えないよ」ってことかな?
const string& getName()
{
 return this->m_name;
}
こんなソース(..cppファイル)だったんですけど、getName()の前にクラス名が抜けてるだけでした。

2010年5月1日土曜日

C++でenumにtoStringを実装してみる

今日は、C++でenumtoString()を実装してみたいと思います。
え!?、出来るのかって?クラスでカプセル化するだけですがなにか・・・?
ってことでクラス図です。
状態(State)を管理するenumを適当に宣言してクラスのメンバ変数としてます。
んでもってtoString()メソッドを宣言してあります。
 つぎにソースを見てみましょう、用意するのは次のファイルです。
  • main.cpp
  • EState.h
  • EState.cpp
まずはEState.hです。
#ifndef _E_STATE_H_
#define _E_STATE_H_

enum State {    //  ①
    WAIT_INIT = 0,
    IDLE,
    PLAY,
    PAUSE,
};

class EState {
    //----------------------------------
    //  フィールド
    //----------------------------------
private:
    State m_state;  //  状態

    //----------------------------------
    //  プロパティ
    //----------------------------------
private:    //  ②
    //  状態取得
    const State getState() const ;
    //  状態設定
    EState& setState(const State _state);

    //----------------------------------
    //  メソッド
    //----------------------------------
public:
    //  コンストラクタ
    EState();
    EState(const EState& _state);   //  ③
    EState(const State _state);

    //  デストラクタ
    ~EState();

    //  operator
    operator State() const; //  ④
    EState& operator=(const EState& _state);    //  ⑤

    //  toString
    const char* toString() const;   //  ⑥
};
#endif 
①enumを宣言しています。
②状態のgetter、setterですがインタフェースを減らすためにprivateにしてあります。
③コピーコンストラクタです。
④キャストoperatorです。これによりEStateからStateへの代入を可能にしています。後ろにconstが着いているのはconst化された変数からも呼び出せるようにです。
⑤代入operatorです。
⑥toString()メソッドです。

つぎに、EState.cppです。
#include "EState.h"

//----------------------------------
//  プロパティ
//----------------------------------
//  状態取得
const State EState::getState() const
{
    return this->m_state;
}
//  状態設定
EState& EState::setState(const State _state)
{
    this->m_state = _state;
    return *this;
}

//----------------------------------
//  メソッド
//----------------------------------
//  コンストラクタ
EState::EState() :
    m_state(WAIT_INIT)  //  ①
{
    // do nothing...
}
EState::EState(const EState& _state) :
    m_state(WAIT_INIT)  //  ①
{
    *this = _state;
}
EState::EState(const State _state) :
    m_state(WAIT_INIT)  //  ①
{
    this->m_state = _state;
}

//  デストラクタ
EState::~EState()
{
    //  do nothing...
}

//  operator
EState::operator State() const
{
    return this->m_state;
}
EState& EState::operator=(const EState& _state)
{
    this->m_state = _state.m_state;
    return *this;
}

//  toString
const char* EState::toString() const
{
    static const char* strArray[] = {
        "WAIT_INIT",
        "IDLE",
        "PLAY",
        "PAUSE",
    };

    return strArray[this->getState()];  //  ②
} 
初期化指定子で初期状態を設定しています。
②対応する文字列を返却しています。なにげないコードですがデータとロジックを分けることによってバグが少なくメンテナンスが容易になっています。

最後にmain.cppです。
#include 
#include "EState.h"

void main(void)
{
    const EState state; //  ①
    printf("state:%s\n", state.toString());   //  ②
    const State tempState = state;  //  ③

    printf("hit any key...");
    while(getchar() == EOF){}
}
①実装したEStateクラスを使用してstateを定義しています。
②toString()メソッドを呼び出して出力しています。
③キャストoperatorを実装しているのでEState型からState型への代入も可能です。

以上ですが、たかがtoString()の為だけにこれだけのコードを書かないと行けないC++ってやっぱり生産性が低いのかな・・・?

C++のコンパイルエラー:**** was not declared in this scope

今日、仕事中に出たエラーです。
「****はスコープ内に宣言されてないよっ」てことのようです。
実際、何が問題だったかというと、
****はクラス名だったのですが、スペルミスでした(汗。