フリーソフト

« ヘッダファイル | トップページ | クラス »

スコープ

このドキュメントは、Google C++ Style Guideを翻訳、要約したものです。省略した部分や意訳した部分が多くあります。原文の意図が伝わるよう注意しましたが、誤りが含まれているかもしれません。正確な情報については、必ず原文を参照してください。

前へ | 目次 | 次へ

スコープ

名前空間

.ccファイルでは名前なし名前空間の使用を推奨する。名前付き名前空間の名前は、プロジェクト名と、可能ならパス名に基づいたものとする。usingディレクティブ、インライン名前空間は使用禁止。以下に名前空間の使用のポリシーを示す。

名前なし名前空間

  • 名前衝突を避けるため .ccファイルでは名前なし名前空間の使用を推奨する。
  • // .ccファイル
    namespace {
    // この関数は、リンク時に他のシンボルと衝突しないことが保証される。
    // この関数は、この.ccファイルの中からしか見えない。
    bool UpdateInternals(Frobber* f, int newval) {
       ...
    }
    }  // namespace コメントで終端すること
    

    特定のクラスに関連するスコープは、クラスのスタティックデータメンバやスタティックメンバ関数、型として宣言したほうがよい。

  • .hファイルでは名前なし名前空間を使用してはならない。

名前付き名前空間

  • インクルード、gflags定義と宣言、他の名前空間にあるクラスの前方宣言を記述した後、残りのソースファイル全体を名前空間でラップする。
  • // .hファイル
    namespace mynamespace {
    // 名前空間スコープの中にすべての宣言を記述
    class MyClass {
    public:
      ...
      void Foo();
    };
    }  // namespace mynamespace
    
    // .ccファイル
    #include "a.h"
    
    DEFINE_bool(someflag, false, "dummy flag"); // gflagsの定義
    
    class C;  // グローバル名前空間のクラスCの前方宣言
    namespace a { class A; }  // a::Aの前方宣言
    
    namespace mynamespace {
    // 名前空間のスコープの中に関数の定義を記述
    void MyClass::Foo() {
      ...
    }
    }  // namespace mynamespace
    
  • 名前空間stdに何かを定義してはならない。標準ライブラリクラスの前方宣言も禁止。stdのエンティティを宣言する場合は、適切なヘッダをインクルードすること。
  • 名前空間のすべての名前を利用可能にするようなusingディレクティブは使用禁止。
  • // 禁止 - 名前空間を汚染する
    using namespace foo;
    
  • .ccファイル内と、.hファイルにおける関数、メソッド、クラスの中であれば、using宣言を使用してもよい。
  • using ::foo::bar;
    
  • ccファイル内、.hファイルの全体をラップする名前付き名前空間の中、関数、メソッドの中であれば、名前空間エイリアスを使用してもよい。
  • // .ccファイルで使うエイリアス
    namespace fbz = ::foo::bar::baz;
    
    // .hファイルで使うエイリアス
    namespace librarian {
    // このエイリアスは、このヘッダをインクルードするすべてのファイルで
    // 利用できる(名前空間librarianの中)。
    // エイリアス名はプロジェクト内で一貫したものを選ぶこと。
    namespace pd_s = ::pipeline_diagnostics::sidetable;
    
    inline void my_inline_function() {
        // 関数(メソッド)内でだけ有効なエイリアス
        namespace fbz = ::foo::bar::baz;
       ...
    }
    }  // namespace librarian
    

    .hファイルに書かれたエイリアスは、このファイルをインクルードするすべてのファイルから見えることに注意。そのため、パブリックヘッダ(プロジェクトの外で利用できるヘッダ)や、それらにインクルードされるヘッダではエイリアスを定義すべきでない。

  • インライン名前空間は使用禁止。

ネストされたクラス

パブリックなメンバクラス(クラス内で定義されたクラス)を作成してはならない。ただし、メンバクラスがインタフェースの一部である場合(あるメソッドのオプションを保持するクラスなど)を除く。それ以外はグローバルスコープを避けるため、名前空間を使用すること。

class Foo {
 private:
 // メンバクラスBar
 class Bar {
  ...
 };
};

非メンバ関数、スタティックメンバ関数、グローバル関数

クラスインスタンスに紐づかない関数が必要な場合は、名前空間内の非メンバ関数や、スタティックメンバ関数を使用すること。グローバル関数は基本的に使用禁止。

非メンバ関数は外部の変数に依存してはならない。また名前空間に常に存在しなくてはならない。スタティック関数をグループ化する場合は名前空間を使うこと。関数間でスタティックデータを共有する必要がある場合はクラスを作成すること。

.ccファイル内でだけ利用できる非メンバ関数が必要な場合は、名前なし名前空間を使用するかスタティックリンク(例:static int Foo() {…})を使用すること。

ローカル変数

変数はできるだけ狭いスコープに置くこと。最初に使用する場所のできるだけ近くで宣言し、宣言時に初期化すること。

if文やwhile文、for文で使用する変数は、文の中で宣言すること。ただし、変数がオブジェクトの場合はループの外側に宣言したほうが効率的である。スコープに入るたびにコンストラクタが呼び出され、スコープを出るたびにデストラクタが呼び出されてしまう。

// 効率的ではない実装:
for (int i = 0; i < 1000000; ++i) {
  Foo f;  // コンストラクタ、デストラクタが1000000回呼ばれる
  f.DoSomething(i);
}
Foo f;  // コンストラクタ、デストラクタは一回だけ呼ばれる
for (int i = 0; i < 1000000; ++i) {
  f.DoSomething(i);
}

スタティック変数とグローバル変数

クラス型のスタティック変数やグローバル変数は使用禁止。生成と解放の順序が不確定で、発見困難なバグを引き起こすため。constexpr宣言された変数は使用してもよい。

静的記憶期間を持つオブジェクト(グローバル変数、スタティック変数、スタティッククラスメンバ変数、関数スタティック変数)は、Plain Old Data (POD)でなくてはならない。PODとは、int型、float型、ポインタ型、PODの配列もしくは構造体。vector(代わりにCの配列を使うこと)やstring(const char []を使うこと)は使用してはならない。

クラス型のスタティック変数は、ビルドするたびにコンストラクタや初期化子の呼び出し順序が変わることさえある。そのため、クラス型のグローバル変数に加えて、関数の結果で初期化されるようなスタティックPOD変数も使用禁止。他のグローバルに依存しない関数(getenv()やgetpid()など)は除く。なお、この禁止ルールは関数スコープ内のスタティック変数には適用されない。それらの初期化順序は明確に定義されているため。

クラス型のスタティック/グローバル変数が必要なときは、main() 関数かpthread_once()でポインタ(freeされないポインタ)を初期化する。これは「スマート」なポインタではなく、ただのポインタとすること。スマートポインタは、解放の順序が不確定となる。

前へ | 目次 | 次へ

« ヘッダファイル | トップページ | クラス »

技術文書」カテゴリの記事