第二回-02 : stack クラスの動作

ここでは stack (スタック) クラスの動作を解説する。
このクラスは本授業にて瀕出する予定であるのでしっかりと理解しておきたい。

まず、スタックを利用したプログラムを以下に挙げる。

#include <iostream>
using namespace std;

// クラス宣言部

#define SIZE 10

// 文字を保存するstackクラスを宣言する
class stack {
  char stck[SIZE]; // スタック領域を確保する
  int tos; // スタック先頭の索引
public:
  void init(); // スタックを初期化する
  void push(char ch); // スタックに文字をプッシュする
  char pop(); // スタックから文字をポップする
};

// クラス実現部

// スタックを初期化する
void stack::init()
{
  tos = 0;
}

// 文字をプッシュする
void stack::push(char ch)
{
  if(tos==SIZE) {
    cout << "スタックは一杯です";
    return;
  }
  stck[tos] = ch;
  tos++;
}
// 文字をポップする
char stack::pop()
{
  if(tos==0) {
    cout << "スタックは空です";
    return 0; // スタックが空の場合はヌルを返す
  }
  tos--;
  return stck[tos];
}

// クラス利用部

int main(){
  stack s1, s2;  // 2つのスタックを作成する
  int i;

  // スタックを初期化する
  s1.init();
  s2.init();

  s1.push('a');
  s2.push('x');
  s1.push('b');
  s2.push('y');
  s1.push('c');
  s2.push('z');

  for(i=0; i<3; i++) cout << "s1をポップする: " << s1.pop() << "\n";
  for(i=0; i<3; i++) cout << "s2をポップする: " << s2.pop() << "\n";

  return 0;
}




スタックとは?

スタックとは、最初に入れたデータが最後に取り出されるようなデータ構造のことを言う。
FILO (First In, Last out: 先入れ後出し)、 LIFO (Last In, Fisrt out: 後入れ先出し) などとも呼ばれる。
具体的には以下の図の様な動作を頭に浮かべれば良い。



この図では、スタックに対して「data1→data2」の順でデータを格納し、 「data2→data1」の順でデータを取り出している様子が描かれている。
なお、スタックにデータを格納することを「プッシュ (push)」、 スタックからデータを取り出すことを「ポップ (pop)」と言う。

なお、スタック自体は抽象的なデータ型であるので、様々な状況で利用され得る。
例えば、マイクロプロセッサの授業を受講した学生ならば、
関数呼出し時のレジスタの一時退避のためにスタックの考え方が利用されていたことを覚えているかもしれない。


stack クラス の概要

本ページの stack クラスは、上記スタックを最も簡単な方法で実装したものと言える。
クラス宣言部 (ヘッダファイル) を見ながらこのクラスの働きを探ってゆこう。

//クラス宣言部
#include <iostream>
using namespace std;

#define SIZE 10

// 文字を保存するstackクラスを宣言する
class stack {
  char stck[SIZE]; // スタック領域を確保する
  int tos; // スタック先頭の索引
public:
  void init(); // スタックを初期化する
  void push(char ch); // スタックに文字をプッシュする
  char pop(); // スタックから文字をポップする
};
このように、慣れて来るとヘッダファイルを見ることでそのクラスの働きがある程度理解できるようになる。

なお、クラス実現部の解説は省略する。各自で考えてみること。


クラス利用部の解説

このページの最後に stack クラスの利用部の解説をする。

//クラス利用部
int main(){
  stack s1, s2;  // 2つのスタックを作成する
  int i;

  // スタックを初期化する
  s1.init();
  s2.init();
  s1.push('a');
  s2.push('x');
  s1.push('b');
  s2.push('y');
  s1.push('c');
// ()
  s2.push('z');

  for(i=0; i<3; i++) cout << "s1をポップする: " << s1.pop() << "\n";
  for(i=0; i<3; i++) cout << "s2をポップする: " << s2.pop() << "\n";

  return 0;
}
なお、コード中 () の位置における 2 つのスタックの様子は以下の図のようになる。



スタッククラス内の配列 stck[] に文字が格納されていることや、変数 tos の値が状況に応じて変化していることがわかる。

ただし、クラス利用部 (main 関数) からは、配列 stck[] や変数 tos は隠蔽されていることに注意しよう。
つまり、作成したクラスを利用する際はクラスの詳細を知る必要がないのである。
これにより、クラス利用者は配列操作の必要がなくなり、潜在するバグの危険性を減らす効果が期待できる。

もちろん、クラスを作成する際 (stack1.cpp を記述する際) は、 そのクラスがバグを引き起こさないよう注意して記述する必要がある。

←第二回-01 : C/C++ プログラミングの基礎第二回-03 : コンストラクタとデストラクタ→

第二回演習用 Web ページへ戻る

クラスから入る C++ へ戻る