プログラミング原人の進化ログ

プログラミング原人の進化論

オレ プログラミング ベンキョウ スル。マナンダ コト カク。

【C++】クラスの使い方

C++のクラスの基本的な使いかたについてまとめました。

対象

クラスについての基本的な知識を持っているけど、C++ではどうやって使えばいいの?という人。

やること

スタックの実装を通して、C++におけるクラスの実際の使い方を学ぶ。クラスの定義から、コンストラクタ・デストラクタあたりまでをやります。

クラスを定義する

次のように定義します。

class クラス名 {
private:
  privateなメンバを宣言
public:
  publicなメンバを宣言
}

メンバとは、そのクラスの持つ変数や関数のことです。それぞれ、メンバ変数メンバ関数と言います。メンバ関数は他の言語ではメソッドと呼ばれることが多いと思います。

定義したクラスのオブジェクトを作るときは、次のように宣言します。

クラス名 変数名;

privateなメンバとpublicなメンバ

  • privateキーワード:クラス内部からのみアクセスできるprivateなメンバを宣言できます。
  • publicキーワード :クラス外部からアクセスできるpublicなメンバを宣言できます。

なお、デフォルトではprivateになるので、privateキーワードは省略可能です。

例:

class Stack {
 private:
  int m_top; // スタックポインタ
  int m_s[100000];  // スタックの実体となる配列

 public:
  int size();        // スタックのサイズを返す
  int top();         // スタックのトップを返す
  void pop();        // スタックのトップを削除する
  void push(int x);  // xをスタックに入れる
  bool empty();      // スタックが空かどうかを返す
};

上の例ではスタックを扱うStackクラスを定義しています。スタックポインタは配列に格納されている最後尾の要素(top)の一つ後ろを指します。

メンバ関数を定義する

Stackクラスのメンバ関数を実際に定義してみます。

int Stack::size() {
  return m_top;
}

int Stack::top() {
  return m_s[m_top];
}

void Stack::pop() {
  if (m_top == 0) {  // under flow
    cout << "Error: stack is empty." << endl;  
  } else {
    m_top--;
  }
}

void Stack::push(int x) {
  if (m_top == sizeof(m_s) / sizeof(m_s[0] - 1)) {
    cout << "Error: stack is full." << endl;
  } else {
    m_s[++m_top] = x;
  }
}

// スタックが空かどうかを返す
bool Stack::empty() {
  if (m_top == 0) {
    return true;
  } else {
    return false;
  }
}

::スコープ解決演算子と呼ばれ、関数がどのクラスに属するのかを示します。

コンストラクタとデストラク

コンストラクはクラスを実体化する(オブジェクトを作る)際に呼ばれます。コンストラクはクラスと同じ名前を持ち、返り値を持たず、型名を指定しません。

デストラクはオブジェクトを破棄する際に呼ばれます。オブジェクトが破棄されるタイミングは、ローカルオブジェクトならばスコープを抜ける時、グローバルオブジェクトならばプログラムが終了する時です。デストラクタは~型名という名前を持ち、返り値を持たず、型名を指定しません。

コンストラクタに引数を渡すこともできます。コンストラクタが引数を持つ場合、オブジェクトを次のように宣言します。

クラス名 変数名(デストラクタの引数)

次の例ではオブジェクトを作る際にスタック番号を渡します。

コンストラクタ・デストラクタを含めたStackクラス:

class Stack {
 private:  
  int m_top; // スタックポインタ
  int m_s[100000];  // スタックの実体となる配列
  int id;    // スタック番号

 public:
  Stack(int id);           // コンストラクタ
  ~Stack();          // デストラクタ
  int size();        // スタックのサイズを返す
  int top();         // スタックのトップを返す
  void pop();        // スタックのトップを削除する
  void push(int x);  // xをスタックに入れる
  bool empty();      // スタックが空かどうかを返す
};

コンストラクタ・デストラクタの定義:

Stack::Stack(int id) {
  m_id = id;
  m_top =0;
  cout << "initialized." << endl;
}

Stack::~Stack() {
  cout << "destroyed." << endl;
}

なお、コンストラクタやデストラクタは、必要ないのであれば、必ずしも定義する必要はありません。上の例では、デストラクタがなくてもスタックの機能には何ら支障はありません。コンストラクタやデストラクタはオブジェクトを作る機能そのものを持つわけではないからです。

スタックを使ってみる

これまでスタックの実装をしてきました。これを使って動作を見てみます。

#include<iostream>
using namespace std;

class Stack {
 private:  
  int m_top; // スタックポインタ
  int m_s[100000];  // スタックの実体となる配列
  int m_id;    // スタック番号

 public:
  Stack(int id);           // コンストラクタ
  ~Stack();          // デストラクタ
  int size();        // スタックのサイズを返す
  int top();         // スタックのトップを返す
  void pop();        // スタックのトップを削除する
  void push(int x);  // xをスタックに入れる
  bool empty();      // スタックが空かどうかを返す
};

Stack::Stack(int id) {
  m_id = id;
  m_top =0;
  cout << "initialized." << endl;
}

Stack::~Stack() {
  cout << "destroyed." << endl;
}

int Stack::size() {
  return m_top;
}

int Stack::top() {
  return m_s[m_top];
}

void Stack::pop() {
  if (m_top == 0) {  // under flow
    cout << "Error: stack is empty." << endl;  
  } else {
    m_top--;
  }
}

void Stack::push(int x) {
  if (m_top == sizeof(m_s) / sizeof(m_s[0] - 1)) {
    cout << "Error: stack is full." << endl;
  } else {
    m_s[++m_top] = x;
  }
}

bool Stack::empty() {
  if (m_top == 0) {
    return true;
  } else {
    return false;
  }
}

int main() {
  Stack s(1); // 実体化

  if (s.empty()) cout << "empty" << endl;
  else cout << "not empty" << endl;

  s.push(0);
  s.push(1);
  s.push(2);

  cout << "size: " << s.size() << endl; // 3
  cout << s.top() << endl;  // 3
  s.pop();
  cout << s.top() << endl;  // 2
  
  return 0;
}

実行結果:

initialized.
empty
size: 3
2
1
destroyed.

参考

ハーバート・シルト(2009)「標準講座C++」柏原正三訳・監修 株式会社トップスタジオ