(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 クラスが生成されるタイミングもスコープで制御できます。
雑多な事はコンパイラに任せ、コンパイラに出来ない事に集中します、最適化にも貢献出来ます。