PowerPoint プレゼンテーション

3章 C++プログラムの構成
2001年11月更新
オブジェクト指向プログラミングの概要
ー オブジェクト指向の考え方を理解する -
オブジェクト指向(Object Oriented Concept)とは?
― ソフトウェアの構成を
もの(Object)を中心に考えるアプローチ方法 ―
《 オブジェクト(もの) 》
情報を記録する変数
他のオブジェクト
機能
変数を操作する固有
のメソッド
他のオブジェクトと
交信するメソッド
メッセージのやりとり
Smalltalk,C++,Javaなど
オブジェクト指向のいくつかの特徴
(1) クラス&インスタンス
共通する
変数名
データと機能(メソッドと呼ぶ)が一緒になったオブジェクト
(=インスタンス)を中心に考える.
クラス 学生
共通する変数名
共通するメソッド名
(メンバ関数名)
― 変数名 ―
学籍番号
氏名
年齢
― メソッド名 ―
学ぶ
本を読む
食べる
会話をする
インスタンス毎に
異なる部分
― 変数 ―
学籍番号= データ値
② 逆にインスタンスは
1
クラスから生成する
ものと考える
氏名
= データ値
2
年齢
= データ値
3
― メソッド ―
学ぶ
本を読む
食べる
会話をする
インスタンス
学生1
インスタンス
学生2
①共通するもの(変数名,メソッド名)
はクラスにまとめ,それを利用する
インスタンス
インスタンス
学生4
学生n
共通する
メソッド名
(2) イベントの発生とメッセージの送受信
イベント(マウスクリックやメニュ―選択,タイマーのタイムアウト等)が生じるとメッ
セージが発生する.何をやるかは,メッセージを受信したオブジェクト側に記述する.
オブジェクトn
オブジェクトa
イベント1の発生
メッセージ「走る」
― 変数群―
身長=データ値1
体重=データ値2
体温=データ値3
学籍番号=データ値4
氏名= データ値5
年齢= データ値6
― メソッド群 ―
歩く
走る
一致するメソッドが
あるば起動する
食べる
他のオブジェクト
他のオブジェクトへの
メッセージ
他のオブジェクトへ
メッセージを送信するイベント
メッセージを受信したとき,やるべき操作を
プログラムする
(3) 変数とメソッドの継承性
いろいろなクラスに共通する変数やメソッドは階層構造の上位クラスのもたせ,
下位クラスはそこから自動的に変数名やメソッド名を引継ぐことができる.
クラス 人間
― 変数名 ―
身長
体重
体温
― メソッド名 ―
歩く
走る
食べる
会話をする
クラス 会社員
― 属性名 ―
社員番号
氏名
― メソッド名 ―
仕事をする
打合せる
学生の1つのインスタンス(オブジェク
ト)
がもつ変数と利用可能なメソッド
「クラス 学生」のインスタンスを生成
継承する
(上位クラスから変数名
やメソッド名を引き継ぐ)
クラス 学生
― 属性名 ―
学籍番号
氏名
年齢
― メソッド名 ―
学ぶ
本を読む
― 変数群 ―
身長=データ値1
「クラス 人間」から
体重=データ値2
継承してきた変数
体温=データ値3
学籍番号=データ値4
氏名= データ値5
年齢= データ値6
― メソッド群 ―
歩く
「クラス 人間」から
走る
継承してきたメソッド
食べる
会話をする
学ぶ
本を読む
(4) 多相性
メソッドは上位クラスと継承関係をもった下位クラスで再定義できる.
② 同じメッセージで,受信側のオブジェクト毎に
操作内容を変えることができる.
aInstance
クラス 人間
― 変数名 ―
身長
体重
体温
― メソッド名 ―
歩く
走る
食べる
会話をする
生成
メッセージ「会話する」
代入
a人間
メッセージ「会話する」
代入
生成
クラス 学生
継承
― 変数名 ―
学籍番号
氏名
年齢
― メソッド名 ―
学ぶ
本を読む
会話をする
// 英会話を意味する
a学生
メッセージ「会話する」
① 再定義できる.
補足
オブジェクト指向によるソフトウェア開発の利点
クラスやオブジェクト間の関連がソフトウェア開発の分析設計段階できると,
後はそれぞれのオブジェクトのメソッドを詳細化して行くだけでよい。
(必要に応じて一時的な変数やメソッドを追加する)
《 クラス図 》
ペット
0..*
《 オブジェクト図 》
愛称
1: 指示を出す
お手
1
お座
人
氏名
指示を出す
:人
・・・
1
メソッド内容を段階的に
詳細化
メソッドを次々と
追加
0..2
家
2: お座り
3: お手
:ペット
Visual Smalltalk Workbench 3.11 にみる
オブジェクト指向プログラミングの基本概念
プログラムはオブジェクト(=インスタンス)とイベント,メッセージだけから構成され
それらを図示しながらプログラムを作る.
②イベントが起こったとき,オブジェク
トに送るべきメッセージを指定する.
部品クラス
ライブラリ
1つのアプリケーション
Windowの
1つのインスタンス
②メッセージ
Menuの1つ
のインスタンス
MenuItemの1つ
のインスタンス
①インスタンスを
生成する
③Openメッセージを受信して,何をやるか
はWindowクラス側に記述されている
DialogBoxの1つ
のインスタンス
イベント
③Openメッセージを受信して,何をやるか
はWindowクラス側に記述されている
MS Visual Studioを利用した
C++プログラミングの方法
1
オブジェクト指向プログラミングはどのような手順で行うか
スパイラル的に繰返し設計しプログラミングしてゆく
第2段階:メッセージの決定
クラス間でやり取りされるメッセージを決める
第3段階:具体的な手順の決定
オブジェクトがメッセージを受取って
何をやるかを決める
オブジェクトp
相互作用
オブジェクトq
オブジェクトs
オブジェクトr
クラスA
クラスB
クラスC
第1段階:クラスの決定
必要なクラスとクラス間の関
連や継承関係等を決める
第1段階:クラスの決定
必要なクラスとクラス間の関連や継承関係等を決める
《 一般的なクラス構成の考え方:MVCモデル 》
アプリケーションに共通
するデータを格納する機能
をもったクラス
Microsoft VC++では,
Documentクラスと呼んでいる
(Modelクラス)
互いに役割を分担した3つのクラス群が協調する形で,1つのプログラムを構成する
入出力表示の機能
をもったクラス
(Viewクラス)
イベントの発生の監視や
実行制御する機能をもった
クラス (Controllerクラス)
Microsoft VC++では,Controllerクラスの機能は
Viewクラスに一緒になっている
◆ “MFC AppWizard”を使うと,アプリケーションプログラムを構成する
基本的な4つのクラスを自動的に生成してくれる
(メーンフレーム画面を作る代表的なアプリケーションのとき)
クラス CView
クラス Cアプリケーション名View
クラス CDocument
必要に応じて
関連をもつ
継承
メインフレームウインドウ内のクライアン
ト領域を管理するクラス
クラス Cアプリケーション名Doc
クラス CFameWnd
アプリケーションで共通して用いるデータ
を格納しておくModelクラスに相当する
継承
クラス CMainFrm
メインフレームウインドウ(メニューバー
を含む)の制御を担当するクラス
クラス CWinApp
継承
継承
クラス Cアプリケーション名App
アプリケーション全体の制御を担
当するクラス.最初に起動する.
◆ 4つのクラスから生成されたインスタンスは,Windowシステムが監視し
ているイベントが発生すると送られてくるメッセージを拾って,いろいろな
処理を行う.
インスタンスを生成する
class
CアプリケーションView
プログラムの世界
CアプリケーションViewの
インスタンス
対応
表示された世界
メインフレーム画面
メニューバー
メニューアイテム1
メニューアイテム2
クライアント領域
インスタンスを生成する
イベントメッセージの転送
class
CMainFrame
インスタンスの生成メッセージ
対応
CMainFrameの
インスタンス
イベントメッセージ
インスタンスを生成する
class
CアプリケーションApp
最初に実行
アプリケーションAppクラス
のインスタンス “theApp”
いろいろなイベント(マウスクリック,ボタンクリック,タイマー等)
2
オブジェクト指向プログラミングはどのように行うか
クラスは,どこに,どのようにして書くのか
◆ クラスはヘダーファイルとCPPファイルに分けて記述する(1)
アプリケーション名View.h
class
CアプリケーションView
アプリケーション名View.cpp
MainFrm.h
class
CMainFrame
MainFrm.cpp
class
CアプリケーションApp
アプリケーション名.h
アプリケーション名.cpp
クラスの変数名やメンバ関数の
プロトタイプ宣言
クラスのメンバ関数の
具体的な内容を定義
◆ ヘダーファイルとCPPファイルに記述される内容(詳しい意味は後述)
Class
{
クラス名
プロトタイプ宣言部
ヘダーファイル(XXX.
h)
・変数名(メンバ変数名と呼ぶ)群の定義
・メソッド名(メンバ関数名と呼ぶ)群のプロトタイプ宣言
・イベントハンドラ関数名群のプロトタイプ宣言
(イベント発生のメッセージを拾って,何らかの処理を行うメンバ関数)
DECLARE_MESSAGE_MAP()
取り込む
// イベントハンドラ関数があるとき記述する
}
#include
“XXX.h”
CPPファイル(XXX.cpp) BEGIN_MESSAGE_MAP
各種メンバ関数の具体的な定義部
イベントメッセージを拾い起動するイベントハンドラ関数の対応付けマクロ
END_MESSAGE_MAP
メンバ関数(=メソッド)とは
クラスに含まれる(=クラス
のメンバ)であることを意味
する.
クラス名::メンバ関数名 // クラスがもつ固有の処理内容の定義
{
}
クラス名::イベントハンドラ関数名
// これもメンバ関数の一種である
/* イベント メッセージを拾って行うべき処理の内容を定義する */
{
}
新しいクラスはどのように記述するか
class CCMDlg : public CDialog
上位クラス名
{
継承性の制御(後で解
public:
説)
CCMDlg(CWnd* pParent = NULL); // 標準のコンストラクタ
enum { IDD = IDD_CM_DIALOG };
CDialog
CString m_input;
CString m_output;
protected:
CCMDlg
virtual void DoDataExchange(CDataExchange* pDX);
OKボタンを押すと自動的にクラスCMDlgのプロトタイプ宣言
HICON m_hIcon;
(ヘダーファイル)とハンドラ関数ファイル(cpp)が Visual
// 生成されたメッセージ
マップ関数
Studioによって自動的に生成される
//{{AFX_MSG(CCMDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
上位クラス
afx_msg void OnButtonA();
の意味
afx_msg void OnButtonABar();
afx_msg void OnButtonB();
リソースエディタで例えば”IDD_DIALOG2”
afx_msg void OnButtonBBar();
//}}AFX_MSG
のリソースIDをもつダイアログを作成し,
DECLARE_MESSAGE_MAP()
メニュー“挿入”から“クラスの新規作成”
};
を選ぶとこのようなダイアログが表示される
【補足】 クラスからインスタンスを作成したとき,メンバ変数を初期化したいとき
メンバ変数を初期化する特殊なメンバ関数=コンストラクタ
クラス名とメンバ関数名が同じ
初期化するとき,初期値をパラメータで指定してもらうときもある
CSpeedDlg::CSpeedDlg(CWnd* pParent /*=NULL*/) : CDialog(CSpeedDlg::IDD, pParent)
{
m_resist = 0.0f;
m_speedX = 0.0f;
クラスCSPeedDlgのインスタンスを作成するとき,
m_speedY = 0.0f;
CSPeedDlgで定義されたメンバ変数の値を初期化する.
m_speedZ = 0.0f;
}
CDialog
CSpeedDlgはCDialogクラスからもメンバ変数を継承する.
CSpeedDlg
そこでCDialogのメンバ変数の初期化は,CDialogの初期化
CDialogのコンストラクタを呼び出して任せる
第2段階:メッセージの決定
イベントメッセージを拾って起動すべきハンドラ関数名やクラス間
(あるいはクラス内)で呼出しあうメンバ関数名を決める.
イベントが発生したとき送ら
れてくるイベントメッセージ
このクラスで拾うイベント
メッセージがあれば,そ
のイベントメッセージに対
応するイベントハンドラ関
数を呼出す
#include
“XXX.h”
BEGIN_MESSAGE_MAP
各種メンバ関数の具体的な定義部
イベントメッセージを拾い起動するイベントハンドラ関数の対応付けマクロ
END_MESSAGE_MAP
クラス名::メンバ関数名 // クラス固有のがもついろいろな処理内容の定義
{
}
クラス名::イベントハンドラ関数名1 // これもメンバ関数の一種である
/* 送られてきた イベントメッセージを拾って,そのイベントに対応する
処理内容を定義する */
必要に応じて呼出す
{
他のメンバ関数の呼び出し;
クラス名::メンバ関数名;
}
// 他のクラスのメンバ関数の呼び出し
◆ GUIを構成する要素(オブジェクト)には,すべてリソースIDが付いており,実行時
にリソースで定義したリーソスにイベント(例えばクリック)が発生すると,リソースID
をもったイベントメッセージがWindowsシステムから送られてくる
リソースID
イベントメッセージ名
ON_ COMMAND + IDC_SETTIMER
リソースIDは, リソースエディタでそれぞれの
リソースオブジェクトに対して名付けた名前
◆ インスタンスがイベントメッセージを受取ったとき,
どのイベントハンドラ関数を呼び出すかはMESSAGE_MAPで対応つけている
(※MESSAGE_MAPはVC++が自動的に作成する)
CPPファイル(XXX.cpp)
#include “XXX.h”
BEGIN_MESSAGE_MAP
// メベントメッセージを拾って起動するイベントハンドラ関数の対応付け
ON_COMMAND ( IDC_KILLTIMER, OnKillTimer )
イベントメッセージ名
の切り分けマクロ名
リソースID
呼び出すイベントハンドラ
関数名
END_MESSAGE_MAP
イベントメッセージ
ON_COMMAND_ID
C_KILLTIMERが来
るとOnKillTime関数
を呼出す
クラス名::メンバ関数名
// 関数の具体的な内容を定義
{
}
クラス名:: OnKillTimer // イベントハンドラ関数の具体的な内容を定義
{
}
クラス名::OnリソースID_n // イベントハンドラ関数の具体的な内容を定義
{
}
◆ イベントメッセージは,次々と転送される途中で各クラスのメッセージマップに
同じものがあれば拾われ,対応するイベントハンドラ関数を起動する
メッセージマップを見る
CTestDialogのインスタンス
(ダイログボックスに対応)
class
構成上のリンク
メッセージマップを調べる順番
メッセージマップに拾うべきイメントメッセージ名
があれば,イベントハンドラ関数を起動
メッセージマップを見る class
CTestViewのインスタンス
(クライアント領域に対応)
メッセージマップを見る
CMainFrameのインスタンス
(メイン画面に対応)
class
メッセージマップを見る
CTestAppの
インスタンス theApp
class
CTestンApp
メッセージマップに拾うべきイメントメッセージ名
があれば,イベントハンドラ関数を起動
ON_COMMAND + オブジェクトID
メインフレーム画面
メニューアイテム1
メニューアイテム2
メニュー
メニューバー
ダイアログボックス
フィールド1
Windows システム
CMainFrame
メッセージマップに拾うべきイメントメッセージ名
があれば,イベントハンドラ関数を起動
イベントメッセージ
WM_コマンド名 + パラメータ
CTestView
メッセージマップに拾うべきイメントメッセージ名
があれば,イベントハンドラ関数を起動
構成上のリンク
構成上のリンクにしたがって転送する
CTestDialog
操作イベント
ボタン1
ボタン2
◆ ClassWizardを使うと,クラスで拾うべきイベントメッセージ(=
イベントの種類とイベントが発生したリソースID)と呼出すイベントハンドラ
関数名の指定が,半自動的できる.
どこのクラスで拾うかの指定
拾うべきイベントの種類
イベントが発生したリソースID
イベントメッセージを拾ったとき実行するハンドラ関数名
イベントメッセージ名
第3段階:具体的な処理手順の決定
インスタンスがイベントメッセージを受取ったり, 他から呼出さ
れたとき 何をやるかの具体的な内容を決める
#include “XXX.h”
CPPファイル(XXX.cpp)
BEGIN_MESSAGE_MAP
/END_MESSAGE_MAP
クラス名::メンバ関数名
// 関数の具体的な内容を定義
{
}
クラス名:: OnKillTimer // イベントハンドラ関数の具体的な内容を定義
{
}
クラス名::OnリソースID_n // イベントハンドラ関数の具体的な内容を定義
{
}
◆ メンバ関数内で新たにクラス(例えばDialog)のインスタンスを
作りたいとき,どのように記述するか.
#include “CMDlg.h” // <=
float
x;
CCMDlg
// <=
dlg;
dlg.DoModal();
CMDlgの宣言部分をあらかじめ読み込み
float型変数xのメモリ領域を確保する
/* <= クラスCCMDlgからインスタンスdlgを作成し,
ダイアログのメモリ領域を確保する
*/
// ダイアログを実際に表示するメンバ関数を呼び出す.
Dialogクラスを作成したときのク
ラス情報を格納したファイル名
上位クラス
の意味
◆ インスタンがもつ変数,メンバ関数へのアクセス方法
インスタンス名の後にドット(.)をつけて,変数名,メソッド名を記述する
《 インスタンス g がもつ
変数とメンバ関数 》
― 変数群 ―
float
float
X;
float
Y;
float
m_resist;
m_speed
変数のアクセス方法
g.m_speedX = 10.0; // 構造体のアクセスと同じ
m_speed
m_speedZ;
― メンバ関数群 ―
void addVelocuty();
void subtVelocity();
メンバ関数のアクセス方法
g.addVelocity( );
// 構造体のアクセスと同じ
【上級編】
--- プログラムの中で必要に応じて,動的にインスタンスを生成したいとき.---
CMsgDlg * pDlg;
/* <=
作成すべきクラスCMsgDlgのインスタンスへの
ポインタを格納しておくメモリ領域を確保する */
:
pDlg = new CMsgDlg(); /* <=
クラスCMsgDlgのインスタンスを作成し,
そのインスタンスへのポインタをpDlgに格納する.*/
pDlg-> DoModal();
/* メンバ関数の呼び出し
*/
◆ ClassWizardを用いると,GUIを構成するオブジェクトのリソースIDと
プログラム中のクラス内変数名とを簡単に対応されることができる
プログラムの世界
class CSpeedDlg : public CDialog
{
public:
CSpeedDlg(CWnd* pParent = NULL);
enum { IDD = IDD_DIALOG1 };
リソースID: IDC_SPEEDX
float
m_speedX;
float
m_speedY;
リソースID: IDC_SPEEDY
float
m_speedZ;
GUIを構成するオブジェクトの世界
リソースID: IDC_SPEEDZ
リソースID: IDC_RESIST
float
m_resist;
VC++でプログラミングするときの一般的な注意
◆ ヘルパー(お助け)関数
C++のメンバ関数の中でも,Cの関数を補助的に使える
ヘルパー関数
C言語関数の定義
※(注) 識別子の有効範囲が異なるため
この中ではクラスのメンバ変数やメンバ関数は
使用できない.
クラス名::メンバ関数名
{
関数の呼び出し(...);
}
// 関数の具体的な内容の定義
◆ 呼出し可能なメンバ関数と継承性を利用して上位クラスのアクセス
を制御する Private,Protected,Public
Class
完全に外部から隠される
A
Private
上位のクラス
Class
Private
メンバ変数群
メンバ変数群
メンバ関数群
Protected
外部からのアクセス
メンバ変数群
メンバ関数群
外部からのアクセス
Public
継承によるアクセス
メンバ関数群
クラス内部でのみ
アクセスは可
継承によるアクセス
Protected
メンバ変数群
メンバ関数群
Public
メンバ変数群
メンバ変数群
メンバ関数群
継承によるアクセス
クラス外部からのアクセス
B
メンバ関数群
クラス外部からのアクセス
3
オブジェクト指向プログラミングの仕掛けを理解しよう
【補足1】 C++プログラムの基本的な動作手順
class
CWinApp
WinMain(・・・・)
InitApplication関数
①theAppのもつ
アプリケーションの初期化
Run関数
③ theAppのもつイベントメッ
セージを取出す無限ループ
上位クラスのメンバ関数を継承
class
theAppに対して
1: InitApplication関数,
2: InitInstance関数,
3: Run関数の呼出し
アプリケーション・オブジェクト
インスタンス theApp
Cクラス名App
// 唯一の Cクラス名App オブジェクト
InitInstance関数
② theAppのもつメインフレーム画面や
ダイログボックスの作成と初期化
Cクラス名App theApp;
CCMAppからインスタンスを生成し,
theAppとする.
【補足2】メインフレーム画面を用いるときのInitInstance関数の処理
class
CView
インスタンスを生成する
上位クラスのメンバ関数を継承
class
CTestView
CTestViewのインスタンス
メインフレーム画面
対応
クライアント領域
インスタンスを生成する
class
CMainFrame
対応
CMainFrameのインスタンス
CWinTestのInitInstance
関数内からインスタンス生成関数
(コンストラクタ)を呼出す
アプリケーションクラス
(ここではCWinTest)のインスタンス theApp
WinMain
1.TheAppの初期化処理を行うためCwinAppの
InitApplication( )関数を呼出す
2.CWinTestのInitInstance関数を呼出す
3.メッセージキューからイベントメッセージ
の取出しと無限ループを形成するCwinAppの
Run( )関数を呼出す
【補足 3】
ダ イアログボックスを用いるときの
InitInstance関数の処理
class
CDialog
上位クラスのメンバ関数を継承
インスタンス生成呼出しを受け,
CCMDialogのインスタンスを生成する
class Cクラス名Dialog
ダイアログボックス
Cクラス名Dialogのインスタンス
CWinTestのInitInstance
関数内からインスタンス生成
関数 (コンストラクタ)を呼出す
対応
WinMain
アプリケーションクラス
(ここではCWinTest)のインスタンス theApp
1.TheAppの初期化処理を行うためCwinAppの
InitApplication( )関数を呼出す
2.CWinTestのInitInstance関数を呼出す
3.メッセージキューからイベントメッセージ
の取出しと無限ループを形成するCwinAppの
Run( )関数を呼出す