C 言語よりお得な C++ その2

(4)クラス
「クラス」は、C++では、大きなトピックです、ここでは、クラスに関連するいくつかの事を紹介します。

・初期化リスト
「クラス」では、「コンストラクター」と呼ばれる特別のメソッドがあります。

class bitmap
{
public:
// コンストラクター
 bitmap() { }
};

これは、ご承知の通りです。
普通、コンストラクター内では、変数の初期化を行います。

class bitmap
{
  int counter_;
public:
// コンストラクター
 bitmap() { counter_ = 0; }
};

「= 0」と値を代入しています。
これは、間違いでは無いのですが、「=」(イコール)で代入するのでは無く、コンストラクターでは、「初期化リスト」を使います。
違いは、初期化リストでは、各オブジェクトのコンストラクターを呼んでいるのに対して、代入では、=オペレーターを呼んでいる事になります、最適化された場合は、殆ど同じになりますが、コンストラクター内では、初期化リストで初期化するようにして下さい。
※詳細な理由については、記しませんので、ご自分で調べて下さい。

class bitmap
{
  int counter_;
public:
  bitmap() : counter_(0) { }
}

・メンバー変数に「_」アンダースコアーを付ける
クラス内のメンバー変数は、引数の変数名などと被らないようにします。
典型的には、「m_counter」などとする事もありますが、これは、ハンガリアンスタイルと言えます、なので、シンプルに後ろに付けるのが好ましいと思えます。
※高橋晶さんから指摘してもらいましたので修正します。
先頭アンダースコアの次に大文字が来る場合は規約違反となります。

※インクルードガードにアンダースコアー
多くの人(C のプログラマーに多い)が、インクルードガードで使うキーワードに、未だにアンダースコアーを使っている人がいます。

func.h の場合
#ifndef __FUNC_H__
#define __FUNC_H__

...

#endif

しかしこれは、規約違反である事を念のため確認しておきます。
※私は、「#pragma once」をお勧めしますが・・

func.h の場合
#ifndef FUNC_H
#define FUNC_H

...

#endif

・引数の void
受け取るパラメーターが無い場合、C では void を使いました。

void init(void)
{
}

C++ では、何も書く必要は無くなりましたので、引数が無ければ何も書きません。

void init()
{
}

※しかし、書いてもエラーにはなりません、問題なのは、書く必要が無いのにあえて書く人と、その心理でしょうか・・・

・引数に標準的な値を代入できる
受け取るパラメーターがあったとして、標準的な値を代入しておく事が出来ます。

void set(int value = 1);

...

  set();     //「1」が引数として使われる。
  set(100);  //「100」が引数として使われる。

これらを利用して、色々と便利な事が行えます、応用してみて下さい。
私がお勧めしたい応用として、ブーリアンを使った、フラグの設定を紹介します。
よく、状態として、「許可」と「不許可」を設定したい場合があります。
そんな時・・・

void enable();
void disable();


のようにしますか?
ですが、これだと、何かの状態を評価してから、状態を設定する場合、関数を呼び分けなければなりません。

if(flag) enable();
else disable();

そこで・・

void enable(bool f = true)
{
}

とすればー

  enable();  // 許可したい場合

...

  enable(false);  // 不許可したい場合

...

  enable(flag);  // flag が「true」か「false」で、「許可」、「不許可」を設定できる。

※よく、二つの状態を受け渡しするのに、「int」とかを使う人がいますが、それは間違いです「bool」を使って下さい。
※又、3つなら「int」が便利(1, 0, -1)と言う人がいますが、それも間違いで、3つの状態があるなら、enum などで、3つの状態を定義して、それを使います。

・enum の便利な使い方

C++ では、define を使わなくなりますし、あえて使う理由もありません、そこで定数を定義するのに便利な enum を C++ で便利に使う為の方法を紹介します。

enum は意味のある値を定義する上で便利な機能ですが、不都合な事が起こります。

たとえば、以下のような、enum の定義では、enum 内に同じ名前のキーワードを定義できません。

enum holizontal {
  LEFT,
  H_CENTER,   // これは少し冗長
  RIGHT
};

enum vertical {
  TOP,
  V_CENTER,   // これは少し冗長
  BOTTOM
};

しかし、C++では、こう書けば・・

struct holizontal {
  enum type {
    LEFT,
    CENTER,
    RIGHT
  };
};

struct vertical {
  enum type {
    TOP,
    CENTER,
    BOTTOM
  };
};

型名をクラス名にして、その中で enum を定義する事で、別々に定義出来ます。
※名前空間で分離する事も出来ますが、私は、クラスで括る方が好みです。
※私は、このような場合に enum の型として type と言うキーワードを使うのが好みです。

int main()
{
  holizontal::type h = holizontal::CENTER;
  vertical::type v = vertical::CENTER;

...

}

※structとclassの違いについて
C++ では、struct と class の違いは、殆どありません、private か、public の違いくらいです。
※ main 関数の戻り値
↑のサンプルでは、main 関数の戻り値(return 0;)が記述されていません、C++ では、main 関数の戻り値が無い場合、0が戻る事が保障されています、これは言語規約に定められています、言いたいのは、C++ では、C 以上に言語規約を良く理解して正確に従う必要がある点です。

・new、delete をなるべく使わない
クラスを定義して、それを使う場合、以前に良く見たサンプルは、以下のような物です。

  func* f = new func;

  f->xxx();

  delete f;

しかし、このように書けば、new、delete を省略出来ます。

  {
    func f;

    f.xxx();

  }

↑のように書けば、delete を忘れて、メモリーリークする事を防げますし、func クラスが生成されるタイミングもスコープで制御できます。
雑多な事はコンパイラに任せ、コンパイラに出来ない事に集中します、最適化にも貢献出来ます。