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

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

C++を常用的に使うようになってから随分時間もたち、C++も少しだけ上達してきました。
最近では殆どのプログラムをC++で行うようになり、組み込みのような小規模なシステムでも、開発環境が提供される限りC++を使っています。

話は変わって、AVRマイコンの開発環境で「WinAVR」と言うのがあります、gcc が使えるフリーのツールキットなのですが、8ビットマイコンであるにも関わらず、C、C++でプログラミングできるフリーの開発ツールです、但し、C++のプログラミングでは、STLは使えないのですが、まぁ、数百バイトのRAMや、数キロバイトのROMでは、リソースの関係で困難と思えます、まして、データ領域とプログラム領域が分離しているアーキテクチャーでは、物理的に同じような実装をする事は難しいのかもしれません。
ただ、string、vector、map のようなコンテナは仕様に制限を設定すれば、実装する事は可能でしょう。

AVRで使える g++ は非常に良く出来ていて、最近では、AVRのプログラミングでもC++でプログラムするようになりました、何か特別な理由が無い限り、Cでプログラムを書く理由は無いと思えます。

しかしながら、典型的なCのプログラマーは、多くの勘違いをしている事が多いと思います、これはC++を本格的に学ぶ前の自分もそうでした。

(1)C++はリソースを多く消費し、プログラムサイズが大きくなる。
(2)C++は余分な処理が多く、余分なマシンサイクルを消費するので、遅い。
(3)Cのプログラミングスキルは、C++にそのまま生かせる。

まず(1)ですが、幾つかのプログラムをつくり、最適化されたアセンブラコードを観てみましたが、適正に書かれたC++コードは、Cのコードと遜色は無く、余分な命令は殆ど追加されていませんでした。
※これは処理系によっては、違う結果となる場合もありますが、WinAVR g++ ではそんな事は無く、洗練されたアセンブリコードが出るようです。

次に(2)ですが、(1)で示したように、余分な命令は殆ど無いので、余分なマシンサイクルも消費しません、場合によっては、より最適化されたコードが出る可能性も含まれている為、C言語より高速なコードが出る可能性もあります。
但し、「適正に書かれた」と言う前提が付きます、適正に書くには「学ぶ」必要がある為、C++に不慣れなプログラマーが、少しのC++のスキルを使って検証したら、別の結論に行き着く可能性があります。

そして(3)です、確かに、何かの処理をどのように細分化して、実際に動くコードを作成する能力(基本的なプログラミングのスキル)は、生かされるのですが、C++には、Cに無い様々な用法があります、それを知らないと、適切なコーディングは出来ないと思うのです、しかしながら典型的なCのプログラマーは、C++をある程度理解しているし、実際に動くコードを実装出来るので、使うつもりなら、いつでもC++でプログラミング出来る(C++は大体理解している)と思っています、しかし、C++はCに似ていますが、全く違う言語である事を認識すべきでは無いでしょうか、C++で適正なプログラミングをする為には、多くの時間を使って学ぶ必要があると思います、もしC++を理解していれば、Cより便利で安全なC++が使える環境でC++でプログラミングしない理由は無いハズなのです、C++を使わないのはそれ相応の理由が存在するのでしょう。
※幾つかの場面で、C++でプログラムしない理由は存在しますが、本当にそうなのかは検証しなければ判らないし、ケースバイケースです、Cを選択するのが最適な場合はあるででしょうが・・

これから、不定期に何回かに分けて、C++でプログラムするのに便利なテクニックや豆知識を紹介していこうと思います。
主に、昔に感じた事や重要と思われるトピックを中心に書いていこうと思います。
この文書は、正確でなかったり、実際と違う場合などがあると思います、気が付いたり指摘されれば修正していこうと思います。

良く、初心者が読むのに適したC++の参考書は?と聞かれますので、とりあえず、この本をあげておきます。
C++ Coding Standards―101のルール、ガイドライン、ベストプラクティス

・名前空間

Cには無い便利な機能として「名前空間」があります、名前空間を活用する事で、プログラムを判りやすく、構造的にする事が出来ます。
※悪名高い Embedded C++ には名前空間がありませんが、C++を理解出来なかった人たち(日本人です)によって策定された仕様です、恥ずかしい事です。

・参照

「参照」は、C++でも最も利益のある機能の一つだと考えます、参照はポインターに似ている為、Cのプログラマーは軽視しがちですが、コンパイラーにとっては、最適化を進める上で、非常に強力な武器です、関数に渡すのがポインターでは無く、参照であれば、より進んだ最適化を行う可能性が生まれます。
また、参照では、ポインターのような、NULLチェックを行う必要は無く、構造的に参照が適用出来ない場合は、コンパイラが教えてくれます。
ポインターより少しだけ制限のある参照は、より洗練された構造をプログラムに提供し、それと同時に安全性も提供します。
参照では、const をより明確に使え、明確な意図をもって伝播させる事が出来ます、これを最初は「ウザイ」と思う人もいますが、そうでは無い事は直に理解出来ると思います。
一つの典型的な方法論として、まず参照で解決出来るか考えて、なるべく参照を使うように全体を設計し、どうしても参照に出来ない場合だけ、ポインターを使うようにします。

ポイント:
NULL について:
「NULL」はC言語のマクロであり、C++でも使えますが使うべきでは無いと考えていますし、使う理由もありません。
C++では「0」を使えば良いのです、NULLを使う理由として、数値に代入する0と、ポインターに代入する0を明確に分けたいとの理由を挙げる人もいますが、それが、わざわざ、C言語のマクロを使う理由としてメリットがあるとは考えられません、また、NULLマクロを使うには stdlib.h(cstdlib)をインクルードする必要があります。

・基本的な事

(1)標準ライブラリーのインクルード
C言語のヘッダーをインクルードする際に
C++では、C言語で使える関数も当然使えます、その際ヘッダーをインクルードしますが、C++用に専用ヘッダーが用意されています。

たとえば、「stdio.h」なら「cstdio」、「stdlib.h」なら「cstdlib」、「string.h」なら「cstring」です。
※規則は察しがつくと思います。
C++の標準的ヘッダーは、「.h」などの拡張子が無いので、それに習っているのと、C++から使う際の「おまじない」がしてあります。
※AVR の g++ では、C++ 専用のヘッダーが用意されていない為、普通のCヘッダーをインクルードします。

(2)型について
C++では、「型」を厳密に評価します。

Cの場合、たとえばポインターは典型的に以下のように書きます。

void func(char *ptr)
{
  char *tmp = NULL;

...


}

しかしC++では・・

void func(char* ptr)
{
  char* tmp = 0;

...


}

ポインターを示す「*」が、変数名に付いていたのが、型に付くようになっています。
Cでは、「ptr」の「ポインター」、「tmp」の「ポインター」だったのが、
C++では、「ptr」や「tmp」は「char」の「ポインター型」と言う考えによるものです。
しかし、多くの人が、自分流の定型記述セオリーを持っており、少しでも異なると、「気持ち悪い」と感じる為、それだけでも、テンションが下がる要因になる場合も少なくありません。
これは、慣れの問題で、もちろんコンパイラーは、「char *ptr」でも「char* ptr」でもエラー無くコンパイル出来ますが、しばらくは、自己流の狭い考えを捨てて、流れに身を任す事が寛容と考えます。

ただ、ここで問題が起こります。

  char* tmp, ptr;

このように書くと、「tmp」は「ポインター型」ですが、「ptr」は「char型」です、これは、C言語との互換を考慮して、このような不都合な事が起こります。
なので、一つの方法として、コンマで区切って、複数の変数を宣言しない事で避ける事ができます。

(3)スコープを利用した宣言

以前、Cの典型的関数では、関数内で使う変数を、頭の方で、集中的に宣言していました。

void func(void)
{
  int i, j, k;
  char c;

...


}

しかし、C++では、変数を使いたい時に、初めて変数を宣言できます。

void func()
{
  int i;
  int j;

...

  char c;
  int k;

...


}

また、スコープを使って、分離する事で、同じ変数名を何度でも宣言できます。

void func()
{
  int j;
  {
    int i;

...

  }

  {
    int i;
    int j;

...

  }
}

これは、コードがより観やすくなるだけでは無く、関数内であってもモジュール化でき、最適化に貢献できます。
ただ、↑の例で、スコープで囲まれた変数「j」を、大域の「j」と混同してしまう場合があり、注意する必要があります。※警告により回避出来ます
その都度宣言する事で、不必要なコードが生成されると思っている人がいますが、最適化されたコードは、そのような事はありません。(コンパイラーの常識、プログラマーの非常識)

OpenGL(with GLFW3)オーディオプレイヤー(ベータ)

OpenGL GLFW3 を使ったオーディオプレイヤーを作ってみました。

まだ、最低限の機能しかありません。

これは、OpenGL で GUI のフレームワークを実現する為のテスト的アプリケーションです。

GUI のフレームワークを作るにしても、何か、アプリケーションを作ってみない事には、必要な要件や仕様などが判らない為です。
GLFW3 を使っているので、Linux や、OS-X でも動作可能と思いますが、現状では Windows 依存のコードが含まれている為、コンパイル出来ません・・

player

・mp3, wav 形式のファイルを再生するアプリケーションです。
・mp3 タグ情報の表示などを行います。
・描画は OpenGL で行っています。
・フォントの描画では、FreeType2 を使っています。
・cygwin の i686-w64-mingw32 クロスコンパイラを使っています。
・左下隅のボタンを押すと、ファイル選択ダイアログが開きます、音楽ファイルを選択して下さい。
・まだベータ版で予期しない動作で落ちる場合もあります(表面的なテストしかされていません)。
・ソースコードは、少し整理してから公開する予定ですが、「今欲しい」方はリクエストを」下さい、方法を考えます。

オーディオプレイヤー

※9月8日15時23分
・大きなメモリーリークを発見したので修正、アーカイブのリンクを修正

※9月9日22時47分
・aac (mp4a) のデコードを追加、タグ情報の表示
・FAAD2 ライブラリーの libmp4ff には、カバー情報(アルバムのアートワーク)を正しく扱えないバグがあり、修正しました。
・その他、微妙に修正

※9月10日22時
GitHub にソースコード一式を公開しました。