株式会社エス・スリー・フォー

8.2 メンバ関数

RWFileManager クラスでは、RWFile クラスのメンバ関数にさらに 4 個のメンバ関数が付け加えられています。それらのメンバ関数は、次のとおりです。

  1. RWoffset allocate(RWspace s); ファイルに s バイトの格納領域を割り当て、割り当て領域の開始点へのオフセットを返します。
  2. void deallocate(RWoffset t); オフセット t で開始する格納領域を解放します。この領域はすでに allocate() 関数によって割り当てられたものでなければなりません。
  3. RWOffset endData(); ファイル中の最後のデータまでのオフセットを返します。
  4. RWoffset start(); ファイルの開始点から RWFileManager によって割り当てられた最初の領域へのオフセットを返すか、領域がまだ割り当てられていない場合には RWNILname=”fn7″ [1]
    を返し、新しいファイルであることを示します。

次のコードをご覧ください。

RWoffset a = F.allocate(sizeof(double));

これは、RWFileManager の F を使って、倍精度のサイズでオブジェクトを格納するために必要な領域を割り当て、その領域へのオフセットを返します。オブジェクトをディスク上のファイルに書き込むには、割り当てられた領域を探し、Write() を使います。ファイルの割り当てられていない領域で読み書きを行うと、エラーが発生します。

格納されているオブジェクトを読み取るのに必要なオフセット値を記憶しておくのは、プログラマの責任です。この負担を軽減するように、RWFileManager が最初に行った割り当ては特別とみなされ、いつでもメンバ関数 start() がオフセットを返すことができます。RWFileManager は、最初のブロックを解放することを許可しません。最初のブロックには、通常、ルートノードのオフセットあるいはリンクリストの先頭など、残りのデータを読み取るために必要な情報が納められています。

次に、RWFileManager クラスを使って、ディスク上に int のリンクリストを作成する例を示します。ソースコードは、fmgrsave.cpp および fmgrrtrv.cpp というファイル名で toolexam サブディレクトリにあります。

この例を使用する場合、挿入する最後の項目をリストに追加するために、そのあとに改行を入力しなければなりません。これは、各コンパイラが cin ストリーム上の EOF の発生を異なる方法で処理するためです。

#include <rw/filemgr.h>                                      // 1
#include <rw/rstream.h>

struct DiskNode {                                            // 2
  int       data;                                            // 3
  Rwoffset  nextNode;                                        // 4
};

main(){
   RWFileManager fm("linklist.dat");                         // 5

   // リンクリストの開始点までのオフセットと同じ大きさの領域を割り当てる:
   fm.allocate(sizeof(RWoffset));                            // 6
   // 最初のリンクに領域を割り当てる:
   RWoffset thisNode = fm.allocate(sizeof(DiskNode));        // 7

   fm.SeekTo(fm.start());                                    // 8
   fm.Write(thisNode);                                       // 9

   DiskNode n;
   int temp;
   RWoffset lastNode;
   cout << "Input a series of integers, ";
   cout << "then EOF to end:\n";

   while (cin >> temp) {                                    // 10
     n.data = temp;
     n.nextNode = fm.allocate(sizeof(DiskNode));            // 11
     fm.SeekTo(thisNode);                                   // 12
     fm.Write(n.data);                                      // 13
     fm.Write(n.nextNode);
     lastNode = thisNode;                                   // 14
     thisNode = n.nextNode;
   }

   fm.deallocate(n.nextNode);                               // 15
   n.nextNode = RWNIL;                                      // 16
   fm.SeekTo(lastNode);
   fm.Write(n.data);
   fm.Write(n.nextNode);
   return 0;
}                                                           // 17

次に、コードの説明を行ごとに示します。

//1
RWFileManager クラスの宣言をinclude します。
//2
Struct DiskNode はリンクリストのリンクです。それには
//3
データ ( int) と
//4
次のリンクへのオフセットが含まれています。RWoffset は通常 long int として型宣言されます。
//5
これは、RWFileManager のコンストラクタで、linklist.dat という新規ファイルを作成します。
//6
最初のリンクへのオフセット値を格納する領域を、ファイル上に割り当てます。この最初の割り当ては特別とみなされ、RWFileManager に保存されます。保存されたオフセット値は、メンバ関数 start() を使っていつでも取り出すことができます。
//7
最初のリンクを格納する領域を割り当てます。メンバ関数 allocate() がこの領域へのオフセットを返します。各 DiskNode は次の DiskNode へのオフセットを必要とするため、現在のリンクが書き込まれる前に、次のリンクの領域が割り当てられていなければなりません。
//8
最初のリンクへのオフセットを書き込む位置を見つけます。この位置へのオフセットは、メンバ関数 start() によって得られることに注意してください。また、RWFileManager クラスは RWFile クラスの派生クラスであるため、fm はRWFile クラスの公開メンバ関数にアクセスできます。
//9
次のリンクへのオフセットを書き込みます。
//10
整数を読み取り、リンクリストにそれを格納するループです。
//11
次のリンクに領域を割り当て、このリンクの nextNode フィールドにその領域へのオフセットを格納します。
//12
このリンクを格納する適切なオフセットを見つけます。
//13
このリンクを書き込みます。
//14
現在のリンクを書き込む前に次のリンクを割り当てるため、リストの最終リンクは、使用されていない割り当てられたブロックへのオフセットを持っています。これは特殊ケースとして処理されなければなりません。
//15
まず、最後の未使用ブロックを解放します。
//16
次に最終リンクのオフセットを RWNIL
に指定します。リストが読み取られるとき、これがリンクリストの最後であることを示します。最後に、正しい情報で最終リンクを再び書き込みます。
//17
RWFileManager クラスのファイルを閉じるデストラクタがここで呼び出されます。

ディスク上にリンクリストを作成しましたが、それをどのようにして読み出すのでしょうか?次に、リストを読み取り、格納されている整数フィールドを表示するプログラムを示します。

#include <rw/filemgr.h>
#include <rw/rstream.h>

struct DiskNode {
  int       data;
  Rwoffset  nextNode;
};

main(){
   RWFileManager fm("linklist.dat");                         // 1

   fm.SeekTo(fm.start());                                    // 2
   RWoffset next;
   fm.Read(next);                                            // 3

   DiskNode n;
   while (next != RWNIL) {                                   // 4
     fm.SeekTo(next);                                        // 5
     fm.Read(n.data);                                        // 6
     fm.Read(n.nextNode);
     cout << n.data << "\n";                                 // 7
     next = n.nextNode;                                      // 8
   }
   return 0;
}                                                            // 9

次に、コードの説明を行ごとに示します。

//1
RWFileManager が既存のファイルで作成されました。
//2
メンバ関数 start() がファイルに割り当てられている最初の領域へのオフセットを返します。この場合、その領域には、リンクリストの開始点へのオフセットが含まれています。
//3
最初のリンクへのオフセットを読み取ります。
//4
リンクリストを通して読み取り、各項目を表示するループです
//5
次のリンクを見つけます。
//6
次のリンクを読み取ります。
//7
整数を表示します。
//8
次のリンクへのオフセットを取得します。
//9
RWFileManager クラスのファイルを閉じるデストラクタがここで呼び出されます。

注釈

  1. ^ RWNIL は、その実際の値をシステムに依存するマクロです。その値は通常 -1L です。