読者です 読者をやめる 読者になる 読者になる

C++ファイル分割

c++

またまた久しぶりにC++を書こうとすると,ヘッダファイル,ファイル分割を忘れてしまっていたので,整理.

全般

  • 共通にするものをヘッダに書く(当たり前だけど).
  • 宣言と定義の違いを持つこと.宣言は名前だけ.定義は実態を生成する文.
  • 定義は(普通)は一つ.ということで関数の定義や,変数の定義は書かない.
  • 逆に変数の宣言(extern)はヘッダに書く.共通したいから.
  • グローバル変数は実態を(何処か1つの)ソースファイルに書いて,そのヘッダにexternで宣言する.他のソースをはそのヘッダをインクルードする.
  • static変数は(普通は)ヘッダに書かない.ヘッダに書くってことはそのヘッダをインクルードしたソース(Cのインクルードは基本的にコピー)全てに,ソース固有のstatic変数が生成される.
  • ヘッダの中で不要にヘッダファイルをインクルードしない.相互依存が発生してしまうし,そのヘッダをインクルードしているソース全部が再コンパイルされてしまう.ラベルが欲しいだけなら前方宣言を利用する.

前方宣言

  • クラス定義をしていると,他のクラスを変数として持ちたいことが(当然だけど)ある.その場合にそのクラスのヘッダファイルをインクルードしないといけなくなって,すぐにヘッダの依存関係が混雑してしまう.
  • そんな時に利用するのが,その変数をポインタとして持つ方法.ポインタであればメソッドやメンバ変数などの情報(というかコンパイル時のメモリ割当)が不要(ポインタ64ビットか32ビットあれば良い)だから,ラベルさえあれば良い.
// シンプルVer.
class Hoge; // <- 前方宣言

class Foo {
private
  Hoge *hoge; // <- ポインタで持つ
};

// 名前空間の下にいるVer.
namespace BAR {
  class Hoge;
}

class Foo {
private
  BAR::Hoge *hoge;
};

extern C {...}

  • Cの関数をC++でも使えるようにするための宣言.
  • 関数名はコンパイル時にリネームされる.しかし,その方法がCとC++で異なるため,普通にコンパイルするとリンクエラー(そんな名前の関数無いよ)になる.
  • それを避けるために,extern C {...}で囲っておく.特に,g++とかだと_cplusplusマクロが定義されているから,それでifdefする.
#ifdef _cpuluspulus
extern C {
#endif
... // Cの宣言文

#ifdef _cpuluspulus
}
#endif