• 検索結果がありません。

文法的な諸注意 (まとめ)

ドキュメント内 新潟大学学術リポジトリ (ページ 75-80)

70 3. C言語構造体の考えの拡張、クラス

3 メンバ関数の定義全体をメンバリスト{...} の中に書いた場合、この関数につい ては暗黙にinline宣言されたものとして処理される。

3 メンバ関数のプロトタイプだけをメンバリスト{...} の中に書いた場合、この関 数の処理本体部を含む関数定義はメンバリスト{...}の外で行う。その際の関数 名は クラス名::メンバ関数名 という名前で表す。

コンストラクタ: クラス定義に際しては、オブジェクトの構成メンバとしてコンストラ クタと呼ばれる特殊な役割の関数を用意することができる。

• コンストラクタは、クラス定義に基づいたオブジェクトを生成する際に初期化のため に自動的に呼び出される。

• コンストラクタの関数名 は クラス名 である。

• 戻り値も常に無いので、関数定義の際に「戻り値の型」の部分には何も書かない。

• オブジェクトの初期化に際して、オブジェクト内のデータメンバの初期化 は、関数 頭部と関数処理本体を囲む波括弧{...}の間に次の形の記述を配置することによって 行うことができる。

: メンバ名1 ( 初期値を表す式1 ), ..., メンバ名n ( 初期値を表す式n )

但し、メンバが基本データ型でなく、何らかのクラス定義に従ったオブジェクトであ る場合には

メンバ名( コンストラクタへの実引数列 ) という形で初期設定の指定を書く。

• 一般に、引数なしで呼び出されるコンストラクタ はデフォルトコンストラクタと呼 ばれ、当該クラスに属するオブジェクトを保持する変数や配列の宣言の際に初期設定 の指定がない場合に暗黙に呼び出される、という特別な役割を果たす。

• 引数が1個のコンストラクタ はデフォルトでは「引数の型→コンストラクタが扱う オブジェクトの型」という型変換に使われる。この型変換を止めたい場合は関数宣言

の前にexplicit修飾子を付ける。

• 一般に、同クラスに属する別インスタンスをコピーすることによってオブジェクトの 初期化を行うコンストラクタをコピーコンストラクタと呼ぶ。

デストラクタ: クラス定義に際しては、オブジェクトの構成メンバとしてデストラク タと呼ばれる特殊な役割の関数を用意することができる。

• デストラクタは、ブロック終了等に伴うオブジェクト消滅の際に後始末(e.g.ヒープ 領域から確保した領域の開放)のために自動的に呼び出される。

• デストラクタの関数名 は ~クラス名 である。

• 戻り値も常に無いので、関数定義の際に「戻り値の型」の部分には何も書かない。

演習問題

□演習 3.1 (eの1000桁計算) 前ターム「プログラミングAI」例題8.4で示した

modules-napier-sub2.c に相当するオブジェクトをインスタンスとして生成するクラスを定義し、

3.5. 文法的な諸注意(まとめ) 71

これを利用して例題8.4で示されたCプログラムと(ほぼ)同等の処理を行うC++プログ ラムを構成せよ。

□演習 3.2 (連想計算;Fibonacci数列の再帰計算を効率的にする) 前ターム「プログラ ミングAI」例題8.5 で示したmodules-fibonacci-sub2.c に相当するオブジェクトを インスタンスとして生成するクラスを定義し、これを利用して例題8.5で示されたCプロ グラムと(ほぼ)同等の処理を行うC++プログラムを構成せよ。

□演習 3.3 (疑似乱数発生) 前ターム「プログラミングAI」例題8.6で示したmodules-random.c に相当するオブジェクトをインスタンスとして生成するクラスを定義し、これを利用して 例題8.6で示されたCプログラムと(ほぼ)同等の処理を行うC++プログラムを構成せよ。

□演習 3.4 (指定されたクラスの定義) 漸化式 an+1 = (12345∗an+ 17)%100 によって定 まる数列 a0, a1, a2, a3, ... を考える。これに関して、

(1) 次の3つのうち2以外を内部に持つオブジェクトをインスタンスとして生成する能 力を備え、また2の様なコンストラクタを持つクラス IntGenerator を定義せよ。

1 データメンバan · · · 数列内で現在注目している要素の値を保持。

2 コンストラクタ· · · データメンバan に数列の第0項の値a0 を初期設定する。

3 メンバ関数 next() · · · データメンバ an 内に現在保持している値に続く、次の 項の値を新しい an の値として設定し、これを戻り値として 返す。

(2) 問(1)で定義したクラス IntGenerator を利用して、a0 = 1 を第0項とする数列 a0, a1, a2, a3, ... の第1項〜第100項の値を出力するC++プログラムを作成せよ。(a0 を出力から除外していることに注意。)

□演習 3.5 (指定されたクラスの定義) 次の(1), (2)の順にC++プログラムを構成せよ。

(1) 次の5つのうち3以外を内部に持つオブジェクトをインスタンスとして生成する能 力を備え、また3の様なコンストラクタを持つクラス Accumulator を定義せよ。

1 データメンバsum· · · これまでに出てきた実数値データの総和を保持。

2 データメンバnum· · · これまでに出てきた実数値データの個数を保持。

3 コンストラクタ· · · データメンバsumを 0.0 に、numを 0に初期設定する。

4 メンバ関数addNewData() · · ·引数で指定された実数データをsumに加算し、num に 1 を加える。

5 メンバ関数getAverage() · · ·現時点でのsum

num の値を求め、これを戻り値として 返す。

(2) 問(1)で定義したクラス Accumulator を利用して、101 P10x=1x1 を計算して出力する C++プログラムを作成せよ。

□演習 3.6 (クラスの利用) 次のC++プログラムを実行するとどういう出力が得られる

か?下の会話の様子中で の部分に予想される出力文字列を入れよ。但し、

解答の際は空白を と明示せよ。下の会話の様子では、下線部はキーボードからの入力 を表している。

72 3. C言語構造体の考えの拡張、クラス

[motoki@x205a]$ cat Q.h

#ifndef ___Class_Q

#define ___Class_Q

#include <string>

class Q { int a, b, c;

public:

Q(int a=0, int b=1, int c=2): a(a), b(b), c(c) {}

int push(int x);

int push(int x, int y);

int push(int x, int y, int z);

std::string config();

};

#endif

[motoki@x205a]$ cat Q.cpp

#include <sstream>

#include <string>

#include "Q.h"

using namespace std;

int Q::push(int x) {

int ans = a;

a = b;

b = c;

c = x;

return ans;

}

int Q::push(int x, int y) {

push(x);

return push(y);

}

int Q::push(int x, int y, int z) {

push(x);

push(y);

return push(z);

} (右上へ続くր)

左下からの続き。) string Q::config() {

ostringstream os;

os << "(a,b,c) = ("

<< a << "," << b << "," << c << ")";

return os.str();

}

[motoki@x205a]$ cat test1808_2b.cpp

#include <iostream>

#include "Q.h"

using namespace std;

int main() {

Q obj1;

cout << "(4)" << obj1.config() << endl;

Q obj2(11, 22, 33);

cout << "(5)" << obj2.config() << endl;

cout << "(6)" << obj2.push(44) << endl;

cout << "(7)" << obj2.push(55, 66) << endl;

cout << "(8)" << obj2.push(77, 88, 99) << endl;

}

[motoki@x205a]$ g++ test1808_2b.cpp Q.cpp [motoki@x205a]$ ./a.out

[motoki@x205a]$

73

4 何をどうクラスとして定義すべきか?

クラス設計の基本方針

クラス設計の例(平面上の点,長方形,文字列, 素数,線形連結リスト,トランプ札の配り手)

4.1 クラス設計の基本方針

プログラミングI(2017)19.5, Pohl(1999)4章の前書き,4.3節の最後

何をクラスとして定義すべきか: オブジェクト指向プログラミングでまず考えなければ ならないことは「どういうクラスを用意するか」である。これに関して、まず注意すべき ことは、

• クラス定義の沿って作られるオブジェクトは、C言語構造体のカプセルに操作方法も 入れて機能拡充を図ったものである。それゆえ、元々、

• オブジェクトの中心に位置するのは、操作方法ではなく、(何らかのモノを表すため に)構造体内に集められたデータ群である、

ということである。従って、プログラム作りの課題があった時、一般的には、(課題で要 求されている処理の記述に取り掛かる前に)まず、

1 課題文中の名詞もしくはそれに関連した物/エージェントの中から適切なもの を選

び、 '

&

$

% 補足:ここでの「適切なもの(名詞)」としては、

3 汎用性のあるオブジェクトに繋がりそうなもの、

3 特殊だけれどもオブジェクト化するとプログラム全体が 互いに独立な部分に綺麗に分割されそうなもの、など

2 その名詞もしくはその関連物/エージェントに相当するモノ1個を表すデータ群を定 める。そして、

3 前ステップ2で定めたデータ群を操作するための関数群を定め、

4 前ステップ∼2 3で定めたデータや関数のそれぞれについて公開/非公開を定める、

というクラス定義の作業を繰り返して、問題領域に固有の抽象データ型 をクラス定義の 形で必要なだけ実現する。

クラスを設計/定義する際の注意:

• 適切なクラス名、メンバ名を付ける。

3 クラス名としては 前段落ステップ1 で選んだ「名詞やその関連物/エージェント に相当するモノ」を意味する英単語(列,名詞句)を採用すべき。

3 データメンバの名前としては その要素の役割に応じた名詞(句)を採用すべき。

3 関数の名前としては 行う操作を意味する動詞(句)を採用すべき。

• クラス内に用意したメンバは可能な限り非公開とする。'

&

$

% その恩恵:非公開メンバについては、内部の実装方法を自由

に変更できる。(どう変更しても、クラスを利用している他 のコードを変更する必要がない。)

• 目先の利用だけでなく、将来の利用を想定して、有用そうな操作は関数として用意し ておく。

74 4. 何をどうクラスとして定義すべきか?

(補足) この講義ノートでは、Java言語の習慣に倣って2つ目以降の単語の頭文字を大文 字にして複数の単語を並べ、名前を構成している。しかし、C++言語では通常Stroustrup の書籍に倣って、単語と単語の間にアンダースコア(_,下線記号)を入れて複数の単語を 並べ、名前を構成する方式をとっている。

ドキュメント内 新潟大学学術リポジトリ (ページ 75-80)