C++によるオブジェクト指向プログラミング入門(コース資料)
対象者
C/C++の基本的な文法を理解している学習者を対象とする。オブジェクト指向プログラミングの初学者から、概念の再確認を行いたい中級者まで幅広く対応している。
講座構成と学習内容
本講座は以下の4つのトピックスで構成される。
- 第1回:クラスとメソッドでは、オブジェクト指向プログラミングの基盤となるクラス定義、コンストラクタ、メソッドの概念を学習する。
classキーワードによるクラス宣言とnew演算子によるオブジェクト生成の基本を習得する。 - 第2回:メソッド定義と呼び出しでは、クラスの構成要素である属性とメソッドの関係を深掘りする。アクセサの役割と
public指定によるアクセス制御について理解を深める。 - 第3回:サブクラス、継承では、オブジェクト指向の重要概念である継承を扱う。クラス階層の構築方法、基本クラスと派生クラスの関係、
protected指定によるサブクラスからのアクセス制御を学習する。具体例として、Ballクラスを継承したColorBallクラスの実装を通じて、属性とメソッドの継承メカニズムを体験的に理解する。 - 第4回:Coding Standards of C++についてでは、C++における標準的なコーディング規約を学習する。可読性と保守性の高いコードを記述するための実践的な指針を習得する。
学習成果
本講座の修了時点で、以下の能力の習得が期待される。
第一に、クラスを用いたプログラム設計ができるようになる。属性とメソッドを適切に定義し、オブジェクトの生成と操作を行う基本的なプログラムを作成できる。
第二に、継承を活用したクラス階層の設計ができるようになる。既存クラスを拡張して新たな機能を追加する派生クラスの作成方法を理解し、コードの再利用性を高める設計が可能となる。
第三に、C++のコーディング標準に準拠した品質の高いコードを記述できるようになる。
1. クラスとメソッド
資料(スライド): co-1. クラスとメソッド [PDF], [パワーポイント]
トピックス:クラス定義, コンストラクタ, メソッド, class, new
演習パート(クリックして展開)
演習:Studentクラスの定義
目的
クラス定義の基本構文を理解し、属性、コンストラクタ、コピーコンストラクタ、代入演算子、デストラクタを含むクラスを自力で作成できるようになる。
前提知識の確認
演習に取り組む前に、以下の内容を理解しているか確認する。
- クラス定義の構文:
classキーワードを用いてクラスを定義する方法 - アクセス指定子:
privateとpublicの役割 - 属性(メンバ変数):クラス内でデータを保持する変数の宣言方法
- コンストラクタ:オブジェクト生成時に呼び出される特殊なメソッド
- コピーコンストラクタ:既存オブジェクトから新しいオブジェクトを生成する際に呼び出されるコンストラクタ
- 代入演算子のオーバーロード:
operator=を定義し、オブジェクト間の代入動作を制御する方法 - デストラクタ:オブジェクト破棄時に呼び出される特殊なメソッド
- ヘッダファイルと実装ファイルの分離:宣言を
.hファイルに、実装を.cppファイルに記述する方法
演習の手順
- Ball.hを参照し、クラス定義の全体構造を確認する
- Studentクラスのメンバ変数(
int _age;とchar _name[32];)を確認する - Student.hファイルを新規作成し、以下の要素を含むクラス宣言を記述する
#pragma once(インクルードガード)privateセクションにメンバ変数を宣言publicセクションにコンストラクタ、コピーコンストラクタ、代入演算子、デストラクタを宣言
- Student.cppファイルを新規作成し、Student.hで宣言した各メソッドの実装を記述する
- 文字列のコピーには
strcpy関数を使用する(<string.h>のインクルードが必要) - Visual Studioでビルドし、コンパイルエラーがないことを確認する
- 動作確認用のmain.cppを作成し、Studentオブジェクトの生成と破棄が正常に行われることを確認する
ヒント
Ball.hとBall.cppを手本にする:Ballクラスの構造をそのまま参考にできる。Ballクラスではdouble x, yを扱っているが、Studentクラスではint _ageとchar _name[32]を扱う点が異なる。
コンストラクタの初期化子リスト:int型の_ageは初期化子リスト(: _age(age)の形式)で初期化できる。一方、配列型の_nameは初期化子リストでは初期化できないため、コンストラクタ本体内でstrcpyを用いてコピーする。
コピーコンストラクタと代入演算子の違い:コピーコンストラクタは新しいオブジェクトを生成する際に呼ばれ、代入演算子は既存のオブジェクトに別のオブジェクトの値を代入する際に呼ばれる。代入演算子ではreturn *this;を忘れないこと。
デストラクタ:本演習では動的メモリ確保を行わないため、デストラクタの本体は空(/* do nothing */)で構わない。
動作確認:main.cppを参考に、newでStudentオブジェクトを生成し、deleteで破棄するプログラムを作成する。コンパイルと実行が正常に完了すれば、クラス定義は正しく機能している。
よくある間違い
- ヘッダファイルで
#pragma onceを書き忘れる - クラス定義の末尾のセミコロン(
;)を書き忘れる - 実装ファイルでスコープ解決演算子(
::)を書き忘れる(例:Student::Studentと書くべきところをStudentだけにする) - 代入演算子で戻り値の
return *this;を書き忘れる
2. メソッド定義と呼び出し
資料(スライド): co-2. メソッド定義と呼び出し [PDF], [パワーポイント]
トピックス:クラス, 属性, メソッド, アクセサ, public
演習パート(クリックして展開)
例題1:Ballクラスのオブジェクト生成とメモリアドレス表示
目的
C++においてクラスからオブジェクトを動的に生成し、ポインタ変数に格納する方法を理解する。また、->演算子を用いてオブジェクトの属性にアクセスする方法を習得する。
手順
- 新規プロジェクトを作成し、以下の3ファイルを用意する
Ball.h(ヘッダファイル)Ball.cpp(実装ファイル)main.cpp(メイン関数)
- スライドを参照し、
Ball.hにクラス定義を記述する- 属性(メンバ変数)として
double x, yをpublicで宣言する - コンストラクタ(オブジェクト生成時に呼ばれる初期化用メソッド)を宣言する
- コピーコンストラクタ、代入演算子、デストラクタ(オブジェクト破棄時に呼ばれるメソッド)を宣言する
distance_to_0()メソッドを宣言する
- 属性(メンバ変数)として
- スライドを参照し、
Ball.cppに各メソッドの実装を記述する- 初期化子リスト(
: x( x ), y( y )の形式)を用いたコンストラクタの実装に注目する
- 初期化子リスト(
- スライドを参照し、
main.cppを記述するnew演算子でBallオブジェクトを3個生成する- 各オブジェクトのアドレスをポインタ変数
b1,b2,b3に格納する fprintfで各オブジェクトの属性値を出力するdeleteで各オブジェクトを解放する
- ビルドして実行し、実行結果を確認する
ヒント
newで生成したオブジェクトはポインタで受け取るため、属性アクセスには.ではなく->を使用するdeleteを忘れるとメモリリーク(解放されないメモリ領域が残る現象)が発生する- ヘッダファイルの
#pragma onceは、同一ヘッダの重複インクルードを防止するプリプロセッサ指令である
例題2:アクセサの定義と使用
目的
属性をprivateにし、アクセサ(getter)を通じて属性値を取得する設計パターンを理解する。これにより、属性値の意図しない書き換えを防止するカプセル化(データ隠蔽)の考え方を習得する。
手順
- 例題1のプロジェクトをコピーし、例題2用として保存する
- スライドを参照し、
Ball.hを以下のように修正する- 属性
x,yを_x,_yにリネームし、privateに変更する - アクセサメソッド
x()とy()をpublicセクションに追加する - アクセサは
constメンバ関数として定義し、属性値を返すのみとする
- 属性
- スライドを参照し、
Ball.cppを修正する- コンストラクタの初期化子リストで
_x,_yを初期化する - コピーコンストラクタ、代入演算子では
ball.x(),ball.y()のようにアクセサ経由で値を取得する distance_to_0()内でもthis->x(),this->y()を使用する
- コンストラクタの初期化子リストで
- スライドを参照し、
main.cppを修正するb1->xをb1->x()に変更する(属性の直接アクセスからアクセサ呼び出しへ)- 他のオブジェクトも同様に修正する
- ビルドして実行し、実行結果を確認する
public属性とアクセサの違いを確認するpublic属性:外部から直接書き換え可能(リスクあり)- アクセサ:読み取り専用のgetterのみ定義すれば書き換え不可(リスク抑制)
ヒント
- アクセサの命名規則として、属性名から先頭のアンダースコアを除いた名前(
_x→x())を使用している const修飾子がついたメンバ関数は、オブジェクトの状態を変更しないことを保証する- 今回の例題ではgetterのみ定義しているが、setter(属性値を設定するメソッド)を追加すれば、値の検証(バリデーション)を挟むことも可能になる
- クラス内部(メソッド本体)でも直接
_xを参照せずアクセサx()を使用することで、将来的な仕様変更に対応しやすくなる
3. サブクラス、継承
資料(スライド): co-3. サブクラス、継承 [PDF], [パワーポイント]
トピックス:クラス階層, 継承, 派生クラス
演習パート(クリックして展開)
演習1:継承を用いたクラスの実装とオブジェクト生成
目的
継承(親クラスの属性とメソッドを子クラスが受け継ぐ仕組み)を用いて、基本クラス Ball と派生クラス ColorBall を実装し、オブジェクト生成の動作を確認する。
手順
- 新規プロジェクトを作成する(Visual Studio 2019 C++ 等の開発環境を使用)
- ヘッダファイル
Ball.hを作成し、スライドのコードを入力する - ソースファイル
Ball.cppを作成し、スライドのコードを入力する - ヘッダファイル
ColorBall.hを作成し、スライドのコードを入力する - ソースファイル
ColorBall.cppを作成し、スライドのコードを入力する - ソースファイル
main.cppを作成し、スライドのコードを入力する - プログラムをビルドし、実行する
- 出力結果(
b1: 3, 4, 0)を確認する
ヒント
class ColorBall : public Ball という記述は、ColorBall が Ball を公開継承(public inheritance)することを意味する。この記法により、Ball の public メンバおよび protected メンバが ColorBall からアクセス可能となる。
演習2:継承関係の読解と理解
目的
ソースコードを読解し、クラス階層、継承、protected 指定、アクセサ(属性値を取得するためのメソッド)の役割を理解する。
手順
- スライドの図を確認し、Ball と ColorBall の属性の違いを把握する
- スライドの図を確認し、親子関係において子クラスが親の属性・メソッドをすべて持つことを理解する
Ball.hを読み、以下の点を確認するprotected指定された属性_x,_yがサブクラスからアクセス可能であること- アクセサ
x(),y()が属性値を返すこと
ColorBall.hを読み、以下の点を確認するclass ColorBall : public Ballにより Ball を継承していること- 属性
_colorが追加されていること
ColorBall.cppを読み、コンストラクタ内でBall( x, y )を呼び出して親クラスを初期化していることを確認するmain.cppを読み、b1->x(),b1->y()が Ball クラスから継承されたアクセサであることを確認する
ヒント
継承関係において、子クラスのコンストラクタは親クラスのコンストラクタを明示的に呼び出す必要がある。: Ball( x, y ) がその呼び出しに該当する。この初期化リスト(メンバ初期化子リスト)の記法は C++ 特有のものである。
4. Coding Standards of C++ について
資料(スライド): co-4. Coding Standards of C++ について [PDF], [パワーポイント]