第11章 子孫

 今回からは、ちょっと気分を変えて新しいクラスを作っていきます。そして、新しい概念「継承」についての話をしていこうと思います。


 では、今回の要点です。


 では、いってみましょう。


 さて、前回までの CIntArray は一旦置いておいて、今度はファイルを操作するクラスを作ろうと思います。

 ファイルの操作クラスはとても CIntArray と似ているところがあります。ファイルを開くのに失敗すると、ヌルポインタを返してくるあたりがそうです。このあたりの実装は CIntArray ととてもよく似た感じになりますね。

 ということで、CFile クラスを作ってみましょう。モードはバイナリモードで固定したいと思います。ファイル操作については第1部第51章から第55章までを参考にして下さい。

プログラム1
// MainDefs.h
#ifndef __MAINDEFS_H__INCLUDED__
#define __MAINDEFS_H__INCLUDED__

#define numof(array)   (sizeof (array) / sizeof *(array))

#endif
プログラム2
// File.h
#ifndef __FILE_H__INCLUDED__
#define __FILE_H__INCLUDED__

#include <stdio.h>

class CFile
{
    // メンバ変数
private:
    FILE* m_pfile;  // ファイル
    bool  m_bCopy;  // コピーコンストラクタで作られたかどうか

    // コンストラクタ・デストラクタ
public:
    CFile();                     // コンストラクタ
    CFile(const CFile& rother);  // コピーコンストラクタ
    ~CFile();                    // デストラクタ

    // ファイルの開閉
public:
    bool Open(const char* pszFile, const char* pszFlags);
                   // ファイルを開く
    void Close();  // ファイルを閉じる

private:
    bool ModifyFlags(const char* pszSource, char* pszDest, int nSize);
                   // フラグの調整

    // ファイルの操作
public:
    size_t Read(void* pData, size_t nSize);         // ファイルから読み出す
    size_t Write(const void* pData, size_t nSize);  // ファイルに書き込む

    // 評価
public:
    bool IsValid() const;  // m_pfile の値が有効かどうか
    bool Eof();  // ファイルの終端に達したかどうか
};

// m_pfile の値が有効かどうか
inline bool CFile::IsValid() const
{
    return (m_pfile != NULL);
}

#endif
プログラム3
// File.cpp
#include <stdio.h>
#include <string.h>
#include "MainDefs.h"
#include "File.h"

// コンストラクタ
CFile::CFile()
{
    m_pfile = NULL;
    m_bCopy = false;
}

// コピーコンストラクタ
CFile::CFile(const CFile& rother)
{
    m_pfile = rother.m_pfile;
    m_bCopy = true;
}

// デストラクタ
CFile::~CFile()
{
    if(m_bCopy == false)
        Close();
}

// ファイルを開く
bool CFile::Open(const char* pszFile, const char* pszFlags)
{
    Close();

    char bufFlags[8];
    if(ModifyFlags(pszFlags, bufFlags, numof(bufFlags)) == false)
        return false;

    m_pfile = fopen(pszFile, bufFlags);
    return (m_pfile != NULL);
}

// ファイルを閉じる
void CFile::Close()
{
    if(IsValid() == true)
    {
        fclose(m_pfile);
        m_pfile = 0;
    }
}

// フラグの調整
bool CFile::ModifyFlags(const char* pszSource, char* pszDest, int nSize)
{
    bool bBinary;  // 'b' の指定があるかどうか

    // strchr は、指定した文字が初めに出てくる位置を返す関数です
    // ただ単に文字が含まれているかどうかを確認するのにも使えます
    bBinary = (strchr(pszSource, 'b') != NULL);

    // フラグが多すぎるときは false を返す
    if((int)strlen(pszSource) > nSize - 1 - !bBinary)
        return false;

    // 強制的にバイナリモードで開きます
    // strcpy は文字列をコピーする関数、strcat は文字列を追加する関数です
    strcpy(pszDest, pszSource);
    if(bBinary == false)
        strcat(pszDest, "b");
    return true;
}

// ファイルから読み出す
size_t CFile::Read(void* pData, size_t nSize)
{
    if(IsValid() == false)
        return 0;
    return fread(pData, 1, nSize, m_pfile);
}

// ファイルに書き込む
size_t CFile::Write(const void* pData, size_t nSize)
{
    if(IsValid() == false)
        return 0;
    return fwrite(pData, 1, nSize, m_pfile);
}

// ファイルの終端に達したかどうか
bool CFile::Eof()
{
    if(IsValid() == false)
        return true;
    return (feof(m_pfile) != 0);
}

 文字列操作関数については第1部第24章を、void* については第3部第4章を参考にして下さい

 かなり長いのですが、やってることは大したことはありません。コンストラクタ、デストラクタは単純ですし、読み出し、書き込みもほとんどそのまま関数を呼ぶだけです。

 こうして、バイナリモードでファイルを扱うクラスができました。


 さて、次はテキストモードでファイルを扱うことを考えたいと思います。

 テキストモードでは、テキストを扱うのに特有な関数を用意しておくと便利です。しかし、これをバイナリモード時には使う必要はありません。ということで、CFile クラスは CFile クラスで置いておいて、別のクラス CTextFile として作りたいと思います。

 しかし、ファイルを開く部分、読み書きする部分など、CTextFile クラスは CFile クラスと似たようになるはずです。全く別のクラスとして作るのも面倒ですね。CFile を再利用してやることはできないのでしょうか?

 そういうときに使うのが継承です。継承は、あるクラスを元に新しいクラスを作ることです。そして、継承元のクラスを基底クラス、継承してできた新しいクラスのことを、元のクラスの派生クラスと呼びます。

 派生クラスは基底クラスのメンバを全て持っています。基底クラスのメンバ関数を呼んだり、基底クラスのメンバ変数を使ったりできるわけです。

 では、実際にちょっと継承してみましょう。

// TextFile.h
#ifndef __TEXTFILE_H__INCLUDED__
#define __TEXTFILE_H__INCLUDED__

#include <string.h>
#include "File.h"

class CTextFile : public CFile
{
public:
    int WriteString(const char* pszString);  // 文字列を書き込む
};

// 文字列を書き込む
inline int CTextFile::WriteString(const char* pszString)
{
    return Write(pszString, strlen(pszString));
}

#endif

 class CTextFile に続いて、コロンを挟んで public CFile と書いてあります。これが継承の仕方です。

 public と書いてあるのは、派生元の public メンバを派生先でも public にするということです。ここを private にすると、CTextFile のオブジェクトでは Open 関数などが使えなくなってしまいます。

 で、新しくメンバ関数 WriteString を作っています。これは文字列を書き込む関数で、実装は上にあるとおりです。ここでは基底クラスのメンバ関数 Write を呼んでいます。問題なく使えますね。


 ソースが長くなったので、今回はこれで終わろうと思います。内容は少ないんですけどね...。

 それでは、今回の要点です。


 では、次回まで。


第10章 不動の構え | 第12章 第3のアクセス指定子

Last update was done on 2000.8.20

この講座の著作権はロベールが保有しています