ヘッダファイルの書き方 - その1

ライブラリのヘッダファイルの書き方は、特定のアプリケーション専用のヘッダファイルの書き方とそれほど変わりません。違うとすれば、移植性をどの程度考慮するかということと、名前空間の汚染に関する配慮ぐらいかと思います。あとは使い勝手に関してですが、これはアプリケーションの場合でも配慮すべき事柄です。

インクルードガード

C++では、クラス・関数・オブジェクトの定義が同じ翻訳単位に重複して現れるとエラーになります。これは、いわゆる単一定義規則とは別の話で、インライン関数やテンプレートの場合にもあてはまります。具体的には、

struct A
{
  int a;
};

inline void func()
{
}

といった内容のヘッダファイルを、同じ翻訳単位で二回以上インクルードすると、Afunc の定義が重複するためコンパイルエラーになります。これを回避するためのテクニックがインクルードガードで、比較的よく知られています。インクルードガードを施すには、

#ifndef HEADER_H
#define HEADER_H
 
 
#endif  // HEADER_H

のように、ヘッダファイルの内容を #ifndef マクロ名, #define マクロ名#endif で囲みます。処理系によっては #pragma once のような手法を用いることもできますが、移植性はありません。また、インクルードガードに用いるマクロ名は、下線(アンダースコア)で始めたり、連続した下線を含めたりすべきではありません。

記述を重複できるもの、できないもの

ところで、先ほど、クラス・関数・オブジェクトの定義が同じ翻訳単位に重複して現れるとエラーになると書きました。言い換えれば、それ以外のものは、矛盾がないかぎり記述が重複しても問題ないということになります。例えば、定義ではないクラス・関数・オブジェクトの宣言や、型定義(typedef)、マクロ定義(#define)です。

定義ではないクラスの宣言というのは、次のような不完全型の宣言のことです。

struct foo;
union bar;
class hoge;

定義ではない関数の宣言というのは、次のような、いわゆるプロトタイプ宣言です。

void f();
static void g();
extern void h();

定義ではないオブジェクトの宣言というのは、次のような外部宣言のことです。

extern int a;

なお、Cでは問題がなかったオブジェクトの仮定義は、C++では通用しません。例えば、

int a; // 仮定義
int a = 1;

とか、

static int b[]; // 仮定義
static int b[] = { 1, 2, 3 };

などです。

一方、C++では、矛盾がないかぎり型定義(typedef)は重複しても問題ありません。例えば、

typedef int a;
typedef int a;

のように、同じ型定義を複数回記述しても問題ありません。もちろん、C++とCに共通のヘッダファイルを記述する場合はこのようなことはできません。

最後に、意外に知られていませんが、矛盾がないかぎりマクロ定義は重複しても問題ありません。これはCでもC++でも同じです。矛盾がないマクロ定義ですが、完全に一字一句同じでなくてもかまいません。具体的には、空白類の数が異なるだけであれば同じ内容とみなされます。ただし、空白類がまったくないのと1個以上あるのは異なると判断されますので、1個以上であれば何個でも同じとみなされるわけです。

この記事のトラックバックURL:

http://www.kijineko.co.jp/trackback/358