Tools.h++ 7.0 ユーザーズガイド: 7 - クラス RWFileManager の使用法

Copyright 1999 Rogue Wave Software
Copyright 1999 Sun Microsystems, Inc.

# クラス RWFileManager の使用法 | # 7 |

クラス RWFileManager は、ディスクファイルにおいて空き領域の割り当て、解放、合体を行います。これは内部的にはディスク上で空き領域ブロックのリンクリストを保守することによって行われます。

次に示す 2 つの typedef が使用されます。

      typedef long                  RWoffset;
      typedef unsigned long         RWspace;

型 `RWoffset` はファイル内での記憶領域の開始位置までのオフセット用に使用され、`RWspace` は必要な記憶領域の量です。実際の `typedef` は、ユーザーの使用しているシステムによって異なる場合があります。

クラス RWFile はクラス RWFileManager の公開基底クラスですから、クラス RWFile の公開メンバー関数がクラス RWFileManager で使用できます。

## 構築

RWFileManager コンストラクタは次のプロトタイプを持っています。

      RWFileManager(const char* filename);

引数は RWFileManager が管理するファイルの名前です。ファイルが既に存在する場合には、その中には有効な RWFileManager が含まれていなければなりません。ファイルが存在しなければ、それが生成されます。

## メンバー関数

クラス RWFileManager は、クラス RWFile のメンバー関数に加えて、さらに次の 4 つのメンバー関数を備えています。

1. `RWoffset allocate (RWspace s);`

   ファイル内に `s` バイトの記憶領域を割り当て、その割り当ての先頭までのオフセットを返します。

2. `void deallocate (RWoffset t);`

   オフセット `t` から始まる記憶領域を解放します。この領域は関数 `allocate()` によって前に割り当てられたものでなければなりません。

3. `RWOffset endData();`

   ファイル内の最後のデータまでのオフセットを返します。

4. `RWoffset start();`

   ファイルの先頭から、この RWFileManager で割り当てられた最初の領域までのオフセットを返します。割り当てられている領域がない場合は `RWNIL1` を返します。`RWNIL` はこれが新規ファイルであることを示します。

次の文は、RWFileManager `F` を使用して `double` 型のサイズを持つオブジェクトを格納するのに必要な領域を割り当て、その領域までのオフセットを返します。

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

このオブジェクトをディスクファイルに書き込むためには、割り当てられた位置にシークした後、`Write()` を使用しなければなりません。ファイル内で割り当てられていない位置に読み書きを行うのはエラーです。

格納されたオブジェクトを読み取るのに必要なオフセットのレコードを保守するのもユーザーの責任です。この作業を実行しやすくするために、RWFileManager で行われた最初の割り当ては特殊なものとみなされ、いつでもメンバー関数 `start()` で返すことができます。RWFileManager はユーザーがそれを解放できないようにしています。この最初のブロックには通常、残りのデータを読み取るために必要な情報 (おそらくルートノードのオフセットやリンクリストの先頭) が入っています。

次の例は、ディスク上に `int` 型のリンクリストを構築するために、クラス RWFileManager をどのように使用するかを示しています。このソースコードは、`toolexam` サブディレクトリ内に `fmgrsave.cpp` および `fmgrrtvr.cpp` として入っています。

この例を使用する場合は、挿入したい最後の項目の後にキャリッジリターンを入力して、その項目がリストに必ず追加されるようにする必要があります。これは、コンパイラによって、`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 行ずつ説明します。

`//1` | クラス RWFileManager の宣言を取り込みます。
`//2` | `struct` DiskNode はリンクリスト内の 1 つのリンクです。ここには次の `//3.` と `//4.` が含まれます。
`//3` | データ (`int`)。
`//4` | 次のリンクまでのオフセット。一般に `long int` に対して `RWoffset` が "`typedef`" されます。
`//5` | RWFileManager のコンストラクタ。"`linklist.dat`" という新しいファイルを作成します。
`//6` | 最初のリンクまでのオフセットを格納するための領域をファイル内に割り当てます。この最初の割り当ては特殊なものとして扱われ、RWFileManager によって保存されます。この情報はメンバー関数 `start()` を使用することによって、いつでも取り出すことができます。
`//7` | 最初のリンクを格納するための領域を割り当てます。メンバー関数 `allocate()` は、この領域までのオフセットを返します。各 DiskNode は、次の DiskNode までのオフセットが必要なので、現在のリンクを書き込む前に次のリンクのための領域を割り当てなければなりません。
`//8` | 最初のリンクまでのオフセットを書き込むための位置にシークします。なお、この位置までのオフセットはメンバー関数 `start()` によって返されます。また、`fm` はクラス RWFile の公開メンバー関数へアクセスすることができますが、これはクラス RWFileManager がクラス 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 行ずつ説明します。

`//1` | RWFileManager が旧ファイルで作成されています。
`//2` | メンバー関数 `start()` は、このファイルに割り当てられた最初の領域までのオフセットを返します。この場合、その領域にリンクリストの先頭までのオフセットが入っています。
`//3` | 最初のリンクまでのオフセットを読み取ります。
`//4` | リンクリスト全体を読み取って各エントリを出力するためのループ。
`//5` | 次のリンクへシークします。
`//6` | 次のリンクを読み取ります。
`//7` | 整数を出力します。
`//8` | 次のリンクまでのオフセットを取得します。
`//9` | クラス RWFileManager のデストラクタがここで呼び出され、ファイルをクローズします。

1 `RWNIL` はマクロで、その実際の値はシステムに依存します。一般に `-1L` が使用されます。