C 言語によるオブジェクト記述法 COOL に従って書かれたソースコードには、
読むためのコツがあります。目的に応じて次のいずれかの方法があります。
int main() /* 最初にメインから読む */ { /* 処理 ... */ }Windows プログラムでは、イベントドリブン(ユーザなどのきっかけから 処理を記述する方式)なのでメイン関数はありません。 MFC などのクラス・ライブラリから作られたものは、 メインウィンドウ・クラスの 「3.クラスからスタートして読む」の章を参照してください。
class CMyDlg { /* 最初にメインウィンドウ・クラスから読む */ }; int WinMain() /* クラスと見なす */ { while ( PeekMessage() ) { switch() { case WM_PAINT: /* インターフェイスと見なす */ /* 処理 .. */ } } }以下で詳しく説明していきますが、その前に、そのプログラムがどのように 動作するかを確認できれば、まず、しておいた方がよいでしょう。
ここで書かれていることは、COOL に従ったソースコードの読み方ですが、
COOL に従っていない一般的なソースコードを読む場合にも参考になるでしょう。
メイン関数やある機能を持った関数を読むときのコツを説明します。
項目 2-1 から 項目 2-5 まで順番に進めていきます。
void Class_init() /* 初期化関数の内容は後で理解する */ { /* 他の関数が使えるようにするための処理 ... */ } void Class_finish() /* 後始末関数の内容は後で理解する */ { /* 資源を戻すための処理 ... */ }
/* 関数全体のコメントを読むときも出力に注目 */ int func( int input, int* output ) /* 引数や返り値の中から出力に注目する */ { *output = f( input ); /* 出力は output、こいつが重要★ */ return err; /* エラー値は出力ではない */ }出力は、値として出力するものだけではありません。
void put( STRUCT* st, int data ) /* 構造体のメンバに出力する可能性も */ { fprintf( st->file, "%d", data ); /* メンバ変数のファイルに出力 */ printf( "%d", data ); /* 標準出力(グローバル)に出力 */ }
関数の意味的な構造には、大きく2種類があります。
1.のタイプの関数構造は、関数として必要な処理を実行するために
手順ごとに関数や式を並べたもので、
関数全体をブロックごとに分けることができます。
よいソースコードでは、ブロックごとに適切にコメントがしてあります。
1つ1つのブロックが何を処理して
いるのかを理解していくことで全体を理解することができます。
関数は一般に次のような構造になっています。
void func() { /* 初期化ブロック */ ...; /* 後で必要になるデータを求める処理のブロック */ ...; /* 関数全体のメインとなる処理のブロック ★*/ ...; /* 後始末ブロック */ ...; }ですから、関数全体のメインとなる処理がある、関数の後の方のブロックから 読んでいくと、トップダウンに全体を理解することが容易になるでしょう。
2.のタイプの関数構造は、汎用的な関数を用いて(再利用して)、
その差分を記述したものです。
まず、その汎用関数を理解しなければ、関数全体を理解するのは
かなり難しいでしょう。なぜなら、関数全体のほとんどの処理が、その汎用関数の仕様に
合わせるための処理になるからです。
汎用関数は、上で説明した関数構造の「関数全体のメインとなる処理のブロック」に
存在する傾向が高く、これも関数の後の方のブロックになります。
void func() { /* 初期化ブロック */ ...; /* 汎用的な関数に指定するデータを求める処理のブロック */ ...; /* 汎用的な関数をコール ★*/ ...; /* 後始末ブロック */ ...; }どちらの構造であれ、メインとなる処理のコメントに、目立つ★印などを付けると 後で読みやすくなります。
void func() { int n; n = calc( x ); /* 代入文に注目 */ printf( "%d", n ); calc2( x, &n ); /* アドレス参照に注目、コール先も確認 */ printf( "%d", n ); }
構造体、またはクラスの意味を理解する場合は、
「3.クラスからスタートして読む」の章を参照してください。
void func( STRUCT* st ) /* 構造体(クラス)を理解する */ { int n; /*(コメント)*/ /* コメントから意味を理解する */ STRUCT st; /*(コメント)*/ /* コメントと構造体の内容から意味を理解する */ /* 処理 ... */ }
void main() { func( n, &x ); /* この関数は n や x にどんな処理をしているのか */ }
その際、全体を理解しようとしている関数と、コール先の関数の両方を マルチウィンドウで両方表示するとよいでしょう。
void main() { func( n, &x ); } | /* 関数全体のコメント */ /* ・int n; コメント */ /* ・int* x; コメント */ void func( int n, int* x ) { ... |
構造体やクラス、MFC などのクラスライブラリを用いたアプリケーションを
読むときのコツを説明します。
アプリケーションの場合は、「3-2.クラスのインターフェイスを確認する」項目
から参照してください。(アプリケーションクラスのモデルは自明なので)
項目 3-1 から 項目 3-4 まで順番に進めていきます。
モデルになったものは、クラス名やコメントによって確認できますが、
それでも確認できない場合は、クラスのメインとなる操作か属性を
確認します。操作か属性かは、クラスのタイプによって別れます。
send.c.htm (Knowledge Take! で処理された HTML ソースファイル)
send.c ・〜の処理 ★[Send_go()] ... KnowledgeTake! によって作られた目次 ・〜の処理 [Send_go2()] ... KnowledgeTake! によって作られた目次 1| void Send_go( Send*, int n ) 2| { 3| /* 処理 */ |
send.h
struct _Send { Class src; } void Send_go( Send*, int n ); /* インターフェイス ★*/ void Send_go2( Send*, int n ); /* インターフェイス */ |
send.hpp ( C++ のヘッダファイル )
class Send { void go( int n ); /* インターフェイス */ }; |
インターフェイスを一覧して、そのクラスを用いて何が出来るかについて
確認します。ただし、特定のメンバ関数の内容を理解するのではなく、
どのようなインターフェイスがあるかといった概観を確認します。
具体的には、「2.関数からスタートして読む」の章の
「2-2.関数全体の出力(インターフェイス)を確認する」の項目に書かれていることを
確認します。
内容の理解は、次の項目「2-3.メンバ変数、関連するクラスを確認する」に
書かれていることを確認してから始めるとよいでしょう。
メインとなるインターフェイスには、目立つ★印コメントをつけるとよいでしょう。
ただし、重要な機能のインターフェイスに付けるのではなく、
クラスの役割を理解するために必要な基本的なインターフェイスに付けます。
struct _Send { /* たとえば、src から dst へデータ転送するクラス */ UseClass src; /* ソース(転送元)*/ UseClass dst; /* デスティネーション(転送先)*/ int n; /* 補足情報 */ }メンバ変数の意味を確認するには、この章「3.クラスからスタートして読む」と 同じ方法で行います。つまり、ボトムアップに(全体的なクラスの前に、部分的な クラス(メンバ変数)から)理解することになります。
struct _UseClass { /* Send クラスの1つのメンバ変数のクラスを確認する */ UseUseClass src; } void UseClass_func(); /* インターフェイスまで確認する */汎用的なクラスを利用して(再利用して)差分を実装することがよくあります。
struct _Class { SuperClass reuse; /* 再利用、実装のほとんどはこのクラスの内部 ★*/ int n; }ただし、クラス継承の場合、インターフェイスのみ継承することがあります。 インターフェイスには、非常に抽象的なものもあるので、特に理解しなくても よいこともあります。(C++のみ)
class _Class : public InterfaceClass { /* インターフェイス的な継承 */ int n; }メンバ変数は、常に有効な値が格納されていることが保証されています。 (カプセル化と呼びます)
void Class_func( STRUCT* st ) /* 機能的な関数を理解する */ { int n; STRUCT st; ... }
マクロで実装されているインターフェイスの内容は、ヘッダファイルにあるので
注意してください。
class.h ・マクロの処理 [Class_macro()] ... 目次もヘッダファイルにある 1| #define Class_macro( this, n ) \ 2| SubClass_init( this->sub ) |