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( )関数を呼出す
© Copyright 2025 ExpyDoc