#pragma twice

KAB-studio > プログラミング > #pragma twice > 398 Version 18.11 コントロールとデータのやりとりをするクラスを作る

#pragma twice 398 Version 18.11 コントロールとデータのやりとりをするクラスを作る

前のページへ 表紙・目次へ 次のページへ

 Version 18.11
コントロールとデータのやりとりをするクラスを作る

「前回は標準 C++ ライブラリの iostream 系クラスや std::string クラス
を使用して、 C ランタイムライブラリの代わりの処理をしてみました」
『……でもあまり便利になってないよーな……』
「というわけで、今回はその便利な機能を持つクラスを作ってみましょう」
『ええっ、そんなことできるの?』
「もちろん。まぁ MFC くらい便利にはできないけど、十分便利なクラスが
簡単に作れるから」
『おおー』
「これから作るのは、エディットボックスと値のやりとりをするための
CEditCtrl クラス」
『おお、なんだか MFC っぽくて本格的!』
「まずはこのクラスを作ります。 Version 18.03 ( No.390 ) と同じ方法で
ソースファイルとヘッダーファイルを追加してください。作成するファイル
は以下の2つです」

・EditCtrl.h
・EditCtrl.cpp

「で、これから作る CEditCtrl クラスは、以下のような機能を持っていま
す」

・エディットボックスと一対一で結びつく
・GetInt() メンバ関数で整数値として値を取得する
・SetInt() メンバ関数で整数値をセットする
・IsError() メンバ関数でエラー状態を取得する

「この機能を CEditCtrl クラスに持たせるので、 EditCtrl.h は以下のよ
うになります」

// EditCtrl.h

// CEditCtrl クラス。
class CEditCtrl
{
    // このエディットボックスのウィンドウハンドル。
    HWND m_hWnd;
    // エラー状態。 TRUE ならエラー。
    BOOL m_bIsError;

public:
    // コンストラクタ。
    CEditCtrl( HWND p_hDlgWnd, int p_iId );
    // int 型の値として値を取得します。
    int GetInt();
    // int 型の値をセットします。
    void SetInt( int p_iData );
    // エラー状態を取得します。
    BOOL IsError();
};

『一対一で結びつくから、エディットボックスのウィンドウハンドルを
メンバ変数として持つんだ』
「そういうこと。他にもエラー状態を取っておく m_bIsError メンバ変数も
持っています」
『あとはコンストラクタと、値を取得するのとセットするメンバ関数、エラー
取得するメンバ関数ね』
「 GetInt() 、 SetInt() メンバ関数は Version 16.05 ( No.332 ) の
【getter】【setter】とほとんど同じ」
『なるほど、エディットボックスをメンバ変数と考えるわけね』
「こういうのに合わせた方がメンバ関数の機能が分かりやすいし、引数や
戻り値もすぐ決まるから簡単かな。さて、実際の本体、 EditCtrl.cpp はこ
んな感じ」

// EditCtrl.cpp
#include <Windows.h>
#include <string>
#include <sstream>
#include <strstream>
#include "EditCtrl.h"

// コンストラクタ。
CEditCtrl::CEditCtrl( HWND p_hDlgWnd, int p_iId )
    : m_bIsError( FALSE )
{
    // ウィンドウハンドルを取得します。
    m_hWnd = GetDlgItem( p_hDlgWnd, p_iId );
}

// int 型の値として値を取得します。
int CEditCtrl::GetInt()
{
    // エラーをリセットします。
    m_bIsError = FALSE;

    // エディットボックス用文字列を用意します。
    char pch[256];
    // 文字列を取得します。
    GetWindowText( m_hWnd, pch, 255 );
    // 文字列から数値として取り出すためのストリームを用意します。
    std::istrstream cIStrStream( pch );
    // int 型に変換します。
    int iData;
    cIStrStream >> iData;

    // 入力チェックを行います。
    if( cIStrStream.fail() )
    {
        m_bIsError = TRUE;
        return 0;
    }

    // 値を返します。
    return iData;
}

// int 型の値をセットします。
void CEditCtrl::SetInt( int p_iData )
{
    // 文字列変換するためのストリームを用意します。
    std::ostringstream cOStringStream;
    // 引数を文字列変換します。
    cOStringStream << p_iData;
    // 中にある std::string クラスの、参照を取り出します。
    std::string &rcStringAnswer = cOStringStream.str();
    // それをエディットボックスにセットします。
    SetWindowText( m_hWnd, rcStringAnswer.data() );
}

// エラー状態を取得します。
BOOL CEditCtrl::IsError()
{
    return m_bIsError;
}

『んー、もしかしてほとんど OnEqual() メンバ関数と同じ?』
「そう、 CNewCalcDialog クラスの OnEqual() メンバ関数とかなりの部分
が似ています。違うのは」

・ひとつのエディットボックスだけを対象にしている
・エディットボックスのウィンドウハンドルは m_hWnd メンバ変数を使用
・エラー状態をメンバ変数として保存する

「ってところかな」
『えーっと、まず最初のは、 OnEqual() メンバ関数の時は IDC_E_LEFT 、
IDC_E_RIGHT 、 IDC_E_ANSWER の3つを同時に操作してたけど、このクラス
は一対一の関係だからひとつだけ、ってことだよね』
「そういうこと。だからシンプルになってます」
『次は……そっか、コンストラクタでエディットボックスの
ウィンドウハンドルを取得して、それを使ってるんだ』
「そう、 GetDlgItem() メンバ関数をコンストラクタで呼び出して、
ウィンドウハンドルを m_hWnd メンバ変数に取ってあるんです。それを
使い回すわけ」
『これもさっきの一対一の関係ねー。最後のは……これ?』

// int 型の値として値を取得します。
int CEditCtrl::GetInt()
{
    // エラーをリセットします。
    m_bIsError = FALSE;

// 略

    // 入力チェックを行います。
    if( cIStrStream.fail() )
    {
        m_bIsError = TRUE;
        return 0;
    }

「そう、この部分。まぁ簡単に言うと、 cIStrStream.fail() の結果を代わ
りに取っておく、ってところかな」
『そうしないとエラー情報が返せないんだね』
「 Version 13.05 ( No.241 ) で説明したように、エラーの返し方には色々
方法があるんだけど、今回はエラー状態を取っておくってことで、 
GetLastError() 関数の方法に近いかな」
『まぁ fail() メンバ関数と同じ方法だしね』
「さて、最後に使う側。 NewCalcDialog.cpp は以下のように修正します」

// NewCalcDialog.cpp
#include <Windows.h>
#include <stdio.h>
#include "EditCtrl.h" // この行を追加。
#include "resource.h"
#include "Dialog.h"
#include "NewCalcDialog.h"

// 略

// = ボタンが押された時のイベントハンドラ。
void CNewCalcDialog::OnEqual()
{
    // 各エディットボックス用の、 CEditCtrl クラスの変数を
    // 用意します。
    CEditCtrl cEditCtrlLeft( m_hWnd, IDC_E_LEFT );
    CEditCtrl cEditCtrlRight( m_hWnd, IDC_E_RIGHT );
    CEditCtrl cEditCtrlAnswer( m_hWnd, IDC_E_ANSWER );

    // 入力値を取り出します。
    int iLeft = cEditCtrlLeft.GetInt();
    int iRight = cEditCtrlRight.GetInt();

        // 入力チェックを行います。
    if  ( 
            ( cEditCtrlLeft.IsError() ) ||
            ( cEditCtrlRight.IsError() )
        )
    {
        MessageBox
            ( m_hWnd
            , "入力に誤りがあります。"
            , "エラー"
            , MB_OK
            );
        return;
    }

    // 足した値を IDC_E_ANSWER にセットします。
    cEditCtrlAnswer.SetInt( iLeft + iRight );
}

『お、かなりシンプルになった!』
「エディットボックスとのやりとりを CEditCtrl クラスにまとめたことで
かなりシンプルな感じになりました」
『……でも、これって結局プログラムを移しただけだよね』
「本当にそうかな?」
『え?』

/*
    Preview Next Story!
*/
『こうやって別クラス作るのってめんどい! でも』
「でも?」
『使うと便利で面白いかも』
「そう、それを少しずつ続けると……」
『つづけると?』
「というわけで次回」
< Version 18.12 プログラムはクラスの組み合わせ >
『につづく!』
「それを続けていくことで、大きなプログラムが自然とできるわけです」
『ホントかなー、結局私、そういうの作ってなくない?』
「ギク」
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。