{Pohl(1992)3.4-7節} 先の実装例3.1∼3.2から、もし
1 構造体のカプセルの中に構造体内部のデータを操作する関数を入れることができ、また 2 必要に応じて構造体メンバの隠蔽もできる
3.2. C言語構造体の考えの拡張、クラス 51
様になっていれば、生成されたそれぞれの構造体は実装例3.1で考えたソースファイル
stackA char100.cと同様に、カプセル化され情報隠蔽されたソフトウェア部品として機
能する、と考えられる。この様な考えの下、実際にC++言語では構造体に関する上記
∼1 2の拡張が行われている。
実装例 3.3 (char型データが最大100個入るスタック,部品提供の実装案3) C++言語の 下で、上記∼1 2の拡張に沿って実装例3.2 の構造体定義を書き直した例を次に示す。
[motoki@x205a]$ cat -n StackChar100_ver0struct.h
1 /* 「char型データが最大100個入るスタック」を表す構造体の枠組み */
2
3 #include <iostream>
4 #include <cstdlib>
5
6 struct StackChar100 { 7 private:
8 char element[100];
9 int top;
10 public: //StackChar100型構造体を操作するための関数群 11 void initialize() { top = -1; }
12 bool isEmpty() { return (top < 0); } 13 void pushdown(char c);
14 char popup();
15 };
16
17 void StackChar100::pushdown(char c) 18 {
19 if (++top >= 100) {
20 std::cout << "stack overflow" << std::endl;
21 exit(1);
22 }
23 element[top] = c;
24 } 25
26 char StackChar100::popup() 27 {
28 if (top < 0) {
29 std::cout << "popup from empty stack" << std::endl;
30 exit(1);
31 }
32 return element[top--];
33 }
[motoki@x205a]$
これに関して、
52 3. C言語構造体の考えの拡張、クラス
• C++言語では構造体カプセルの中に関数を入れることができるので、プログラム
11∼14行目 では構造体の構成要素としたい関数を並べている。
• 構造体メンバとした関数の内、処理内容が非常に単純なものについては、11∼12行目 の 様に関数定義を丸ごと構造体定義の中に配置している。これらの関数は(暗黙に)inline 宣言されたものとして扱われ、(もし可能であれば、) コンパイル時にこれらの関数 の呼び出し場所に関数呼び出しのコードではなく関数本体の処理コードが埋め込ま れる。
• 構造体メンバとした関数の内、処理内容が1つの文で表せないものについては、struct
...{...} の部分が長くなって構造体の構成要素の見通しが悪くなるのを避けるため、
13∼14行目 の様にstruct構文の中には関数プロトタイプだけを配置し、本体も含め
た関数定義はstruct 構文の外(17∼33行目)に配置している。
• プログラム17行目,26行目 のStackChar100::pushdown, StackChar100::popup で は、pushdown, popupという名前が構造体StackChar100のメンバ関数の名前である ことをスコープ解決演算子:: を用いて明示している。
• struct構文の外(17∼33行目)で定義された2つの関数については、inline宣言され ていないので、コンパイル時にインライン展開されることはなく、関数の呼び出し場 所に関数呼び出しのコードが埋め込まれる。
• プログラム7行目 のprivate:は、これ以降(別の指示があるまで) のメンバを非公 開にし構造体外からのアクセスを禁止することを宣言している。
• プログラム10行目 のpublic:は、これ以降(別の指示があるまで)のメンバを公開し 構造体外からのアクセスを許可することを宣言している。
• プログラム20行目,29行目 のstd:は、coutとendlが標準ライブラリ内で定義され た名前であることを明示するために付けている。'
&
$
% 補足: ヘッダファイル内に「using namespace std;」という行
を入れると、このヘッダファイルをインクルードするソースファ イル上でも「using namespace std;」というusing指令が(知 らない内に)有効になってしまう。そこで、ヘッダファイルの汎 用性を保つため、ここではusing指令の使用を避けている。
• このソフトウェア部品提供の枠組みを利用した例を次に示す。
[motoki@x205a]$ cat -n reverseWordThroughStack3.cpp
1 /* 文字列を1個読み込み、 */
2 /* それをスタックを用いて反転した後出力するC++プログラム */
3 /* (スタックをC++構造体で実装する版) */
4
5 #include <iostream>
6 #include <string>
7 #include "StackChar100_ver0struct.h"
8 using namespace std;
9
10 int main() 11 {
12 StackChar100 stack;
3.2. C言語構造体の考えの拡張、クラス 53
13 string s;
14
15 cout << "Input a string: ";
16 cin >> s;
17 stack.initialize();
18 for (int i=0; i < s.length(); ++i) 19 stack.pushdown(s[i]);
20 for (int i=0; !stack.isEmpty(); ++i) 21 s[i] = stack.popup();
22 cout << "Reversed string: " << s << endl;
23 }
[motoki@x205a]$ g++ reverseWordThroughStack3.cpp [motoki@x205a]$ ./a.out
Input a string: abc123 Reversed string: 321cba [motoki@x205a]$
ここで、
3 利用例のプログラム12行目 に見られる様に、C++言語では構造体タグをデータ 型名として用いることができる。
• このソフトウェア部品提供方式の利点・欠点は次の通り。
(利点) 同種の部品が複数必要な場合にもうまく対処できる。(単に、構造体を表す変 数を宣言するだけ。)
(利点) スタック領域への操作は用意された4つの関数を通してのみ可能なので、ス タックの特性である「後入れ先出し」(LIFO)の原則が保証される。
実装例 3.4 (char型データが最大100個入るスタック,部品提供の実装案4) スタックを カプセル化され情報隠蔽されたソフトウェア部品として提供するに当たって、先の実装例 3.3では、目的とするソフトウェア部品(オブジェクト)の設計図をstruct構文を用いて 定義できることを示した。同等の定義は次に示す様にclass構文を用いて行うこともで きる。
[motoki@x205a]$ cat -n StackChar100_ver1.h
1 /* 「char型データが最大100個入るスタック」を表すオブジェクトの枠組み */
2
3 #include <iostream>
4 #include <cstdlib>
5
6 class StackChar100 { 7 char element[100];
8 int top;
9 public: //StackChar100型構造体を操作するための関数群 10 void initialize() { top = -1; }
11 bool isEmpty() { return (top < 0); } 12 void pushdown(char c);
54 3. C言語構造体の考えの拡張、クラス
13 char popup();
14 };
15
16 void StackChar100::pushdown(char c) 17 {
18 if (++top >= 100) {
19 std::cout << "stack overflow" << std::endl;
20 exit(1);
21 }
22 element[top] = c;
23 } 24
25 char StackChar100::popup() 26 {
27 if (top < 0) {
28 std::cout << "popup from empty stack" << std::endl;
29 exit(1);
30 }
31 return element[top--];
32 }
[motoki@x205a]$
これに関して、
• 実装例3.3で示したStackChar100 ver0struct.hとの違いは、(コメント以外では) 次の2点だけである。
3 プログラム6行目 のキーワードがstruct からclass に変更された。
3 プログラム6行目 の次の行にあったprivate:宣言が無くなった。
• キーワードstructを使った場合はメンバは原則公開でオブジェクト外からのアクセ スは暗黙に許可される。しかし、キーワードclassを使った場合はメンバは原則非 公開でオブジェクト外からのアクセスが暗黙に禁止されるので、上のプログラムでは 6行目 の次にprivate:宣言を置くのを省略している。
'
&
$
% 補足: 2つのキーワードstructとclassの効果の違いは、オブ
ジェクト外からのアクセスに関する暗黙の設定(公開/非公開)が どうなるかということだけである。
=⇒ プログラム6∼32行目 では、結局は構造体の枠組み/設計図 が定義されていることになる。
一般に、C++言語のstruct構文やclass構文もしくは、これに相当する形で定義され
るソフトウェア部品(オブジェクト)の枠組み/設計図/種類のことをクラスと呼ぶ。ま た、定義されたクラスに対して、そのクラスに属するソフトウェア部品のことをそのクラ スのインスタンスと呼ぶ。