フリーソフト

« スコープ | トップページ | Google特有のマジック »

クラス

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

前へ | 目次 | 次へ

クラス

コンストラクタでやるべきこと

コンストラクタでは、複雑な初期化(失敗する可能性のある初期化、仮想メソッドの呼び出しが必要な初期化など)をしてはならない。

複雑な初期化が必要な場合はファクトリ関数かInit() メソッドを使用すること。

初期化

すべてのメンバ変数に対して、クラス内初期化子(in-class initializer)を用意するか、コンストラクタ(デフォルトコンストラクタでも可)を書かなくてはならない。

クラス内初期化子とは、int count_ = 17やstring name_{"abc"} のようにメンバ変数を宣言することを指す。

明示的なコンストラクタ

暗黙的呼び出しを避けるため、引数がひとつのコンストラクタにはexplicitキーワードをつけること。

2つ目以降のパラメータにデフォルト値が設定されている場合もexplicitをつけること。 例:Foo::Foo(string name, int id = 42)。

コピーコントラクタとムーブコントラクタは例外。また、他のクラスとの透過ラッパーのように意図的に暗黙的な呼び出しをするようなクラスも例外。このような例外はコメントに明記しておかなければならない。

また、引数がstd::initializer_list のみのコンストラクタはexplicitでなくてもよい。

MyType m = {1, 2};
MyType MakeMyType() { return {1, 2}; }
TakeMyType({1, 2});

コピー/ムーブ可能な型

必要な場合のみコピー/ムーブを有効にすること。不要な場合は明示的にそれらを無効にすること(= deleteなど)。

コピーの振る舞い(計算的に複雑なものを含む)が一見して分からないような場合は、コピー可能にしてはならない。コピー可能にする場合は、コピーコンストラクタとコピー代入オペレータの両方を定義すること。コピー可能な型において、ムーブ操作がより効果的な場合はムーブ可能とする。その際はムーブコンストラクタとムーブ代入オペレータの両方を定義すること。ムーブの正当性が明確ならば、ムーブのみ可能な型としてもよい。

スライスのリスクがあるため、派生元のクラスでは代入オペレータやパブリックなコピー/ムーブコンストラクタを定義してはならない(同様に、そういったメンバを持つクラスから派生してはならない)。コピー可能な基本クラスが必要な場合は、仮想メソッドClone()と、protected属性のコピーコンストラクタを定義する。派生クラスでは、このコピーコンストラクタを使ってClone() を実装する。

Delegating/Inheritingコンストラクタ

コードの重複が減らせる場合は、Delegating/Inheritingコンストラクタ(C++11の機能)を使用する。

Delegatingコンストラクタの例。

X::X(const string& name) : name_(name) {
  ...
}
X::X() : X("") {}

Inheritingコンストラクタの例。

class Base {
 public:
  Base();
  Base(int n);
  Base(const string& s);
  ...
};

class Derived : public Base {
 public:
  using Base::Base;  // Baseのコンストラクタがここで再宣言される
};

構造体 vs. クラス

構造体はデータ受け渡しのための受動的なオブジェクトにのみ使用すること。それ以外にはクラスを使用すること。

構造体のデータフィールドには、メソッドを通じてではなく直接アクセスすること。メソッドはデータメンバを設定するためだけに使用すること。例えば、コンストラクタ、デストラクタ、Initialize()、Reset()、Validate() など。これ以上の機能が必要な場合はクラスを使用したほうがよい。迷う場合はクラスを使用すること。

STLとの一貫性を保つため、functorsとtraitsについては構造体を使用してもよい。

構造体とクラスでは、メンバ変数の命名規則が異なることに注意。

継承

継承を多用してはならない。継承よりもコンポジション(インスタンスをメンバにすること)を使った方がよい。継承する場合はpublic継承とすること。

private継承したい場合は、代わりに基本クラスのインスタンスをメンバにすべき。

必要に応じてデストラクタを仮想関数にすること。クラスが仮想関数を持つ場合は、デストラクタを仮想関数にすべき。

protected 宣言のメンバ関数を制限すること。データメンバはprivateにすること。

仮想関数や仮想デストラクタのオーバーライドを、overrideやfinalキーワード(C++11以前のコードはvirtualキーワード)を使って明示的に示すこと。これらの指定子はドキュメント化の役割を果たす。記述子がない場合、読み手は、関数やデストラクタが仮想かどうか判断するため、問題のクラスのすべてのアクセッサをチェックしなければならない。

多重継承

多重継承は、実装を持つ基本クラスが1つで、それ以外の基本クラスが純粋インタフェース(Interface接尾辞で終わるもの)の場合のみ許される。

Windowsでは例外がある。「Windowsのコード」を参照のこと。

インタフェースクラス

以下の条件を満たすクラスは、その名前にInterfaceという接尾辞を付けることができる。ただし必須ではない。

  • Publicな純粋仮想関数(”=0”)とスタティックメソッド、仮想デストラクタのみを持つ。
  • 非スタティックなデータメンバは持たなくてもよい。
  • コンストラクタは不要。定義する場合は、引数なしで、protectedで宣言すること。
  • サブクラスの場合は、基本クラスが上記の条件を満たし、Interface接尾辞を持つこと。

ンタフェースクラスは純粋仮想関数を持つため、直接インスタンス化することはできない。派生クラスのデストラクタが呼び出されるよう、仮想デストラクタを宣言すること。

演算子のオーバーロード

特別な事情がない限り、演算子をオーバーロードしてはならない。また、ユーザ定義のリテラルを作成してはならない。

必要な場合はEquals()のような通常関数を定義すればよい。

「コピー/ムーブ可能な型」「関数のオーバーロード」も参照のこと。

アクセス制御

データメンバはprivateとし、必要に応じてアクセス関数を用意すること(Google Testを使うときはtest fixtureクラスのデータメンバをprotectedにしてもよい)。メンバ変数名はfoo_とし、アクセス関数名をfoo()とする。設定関数が必要な場合はset_foo()とする。例外として、static constのデータメンバはprivateにする必要はない。名前はkFooとすること。

通常、アクセス関数はヘッダファイルにインラインで定義すること。

「継承」「関数名」も参照のこと。

宣言の順序

クラス定義はpublic、protected、privateの順とすること。空のセクションは除くこと。各セクション内における宣言は、一般的に以下の順序とすること。

  • 定数(static constデータメンバ)
  • コンストラクタ
  • デストラクタ
  • メソッド(スタティックメソッドを含む)
  • データメンバ(static constデータメンバを除く)

フレンド宣言は常にprivateとすること。DISALLOW_COPY_AND_ASSIGNマクロを使ってコピーと代入を無効にする場合は、privateセクションの最後(クラスの最後)に置くこと。「コピー/ムーブ可能な型」を参照。

出来る限り .ccファイルにおけるメソッド定義の順番は、宣言の順番と同じにすること。

大きなメソッド定義をクラス定義にインラインで書かないこと。詳細は「インライン関数」を参照のこと。

短い関数を書く

関数は小さく集中的なものが望ましい。

関数が40行を超えたら、関数が分割できないか考えてみること。関数を短くシンプルにキープしておくことで、他人がコードを読んだり修正したりすることが容易となる。

前へ | 目次 | 次へ

« スコープ | トップページ | Google特有のマジック »

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