nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

コードレビューで指摘していただいた箇所まとめ (C++編)

コードレビューで指摘していただいた箇所をまとめて記載したいと思います。

  • ポリモーフィズムのための基底クラスには、仮想デストラクタを宣言すること。なぜならデストラクタが virtual でない場合、親クラスの型のポインタを delete した際には親クラスのデストラクタしか呼ばれないため。
// ~Parent is not virtual.
Parent* parent = new Child();
...
delete parent;  // this always calls ~Parent(); ~Child() is never called.
  • デフォルトコンストラクタの引数はvoidと明示して記載すべき
class A {
public:
    A (void) {}

}
  • 単体テストを書きましょう nprogram.hatenablog.com

  • ヘッダ内では、極力他のヘッダをインクルードしないようにしましょう

    • 2重インクルードや、循環参照(*1)を引き起こします。循環参照問題とは、ヘッダーファイルAがヘッダーファイルBをインクルードし、ヘッダーファイルBがヘッダーファイルAをインクルードする事。
    • クラスの前方宣言を有効活用しましょう。ヘッダの余計なインクルードを減らすことができます。
普通の前方宣言(ClassAのメンバ変数としてClassBを持つ場合)
class classB;
class classA
{
public:
    classA();
    ~classA();
private:
    classB *objB;
};
  • クラス名、メソッド名、変数名をしっかり考えよう
    • 例えば、犬クラスは、犬という情報を表現するクラスになっていますか?犬クラスなのに、犬クラスの中にスマートフォンを操作するメソッドがあってはいけません。( ´∀` )
  • コーディングスタイルが統一しましょう
    • スペース
    • 中かっこの位置
  • for文(繰り返し文)の中に、繰り返すたびに呼び出す必要がないのに、呼び出している処理はありませんか。
    • for文の外に処理を映しましょう
  • constが使えるのに、constを使っていない変数、メソッド引数はありませんか。
    • constが使える場合には、constを使いましょう
    • コンストラクタ初期化子で、メンバ変数を初期化した後、メンバ変数を初期化しない場合は、メンバ変数をconst修飾可能です。
  • ポインタ渡しと参照渡し、どちらでも実現できる場合は、参照渡しにしたほうが良いと思います
    • 参照渡しは、実体がなければ実施できないため、呼び出し先のメソッドで、NULLポインタチェックが必要にならない
  • staticクラスなど、インスタンスを生成しないクラスは、コンストラクタに対して、delete宣言をして、コンストラクタの暗黙定義を明示的に禁止すべきである

  • 例外を投げない場合はnoexceptで関数を宣言すること。noexceptを指定することは大きなメリットがあるが、もしnoexceptを指定した関数に修正を加えてnoexceptを指定しないように変更した場合はクライアントコードを壊す可能性がある。 noexceptは可能なら常に指定すべきだが、今後もnoexceptであり続けると考えられる関数に限るべき。

  • main関数のreturn文は、EXIT_SUCCESSを使うことが望ましい return EXIT_SUCCESS;

  • class宣言をfinal修飾子をつけると、そのクラスは継承不可となる
class BaseClass final   
{  
};  
  
class DerivedClass: public BaseClass // compiler error: BaseClass is   
                                     // marked as non-inheritable  
{  
};  
  • 例外を送出する可能性があるかないかのみを指定する。例外を送出する可能性がある関数にはnoexcept(false)を指定し、例外を送出する可能性がない関数にはnoexcept(true)もしくはnoexceptを指定する
  // getValue()メンバ関数は、例外を送出しない
  int getValue() const noexcept
  {
    return value_;
  }
  • constexprは、「constant expression (定数式)」の略語である。この機能を使用することで、コンパイル時に値が決定する定数、コンパイル時に実行される関数、コンパイル時にリテラルとして振る舞うクラスを定義できる。static constexpr int NUM=10;

  • 条件演算子を含む式では、式ごとにカッコで括ること。⇒ 静的解析ツールで警告が出るため

[OK]
if ( (0 < a) && (a < 10) ) {


[NG]
if ( 0 < a && a < 10) {
  • 全てのクラスオブジェクトで使う定数の宣言するとき, それがリテラル型(int, double, charなど)であればconstのかわりにconstexprを使うこと。クラス定義の中で値を初期化可能。
class nyan {
  constexpr static int i = 1;
  constexpr static double d = 1.2;
  constexpr static char str[] = "nyan!";
};
  • メンバ関数の右側にconstをつけると、そのメンバ関数内ではメンバ変数の変更ができなくなります。このメンバ関数をconstメンバ関数と呼ぶ。
class A
{
public:
    int m_Value;
    void Hoge( void ) const // ←このconstです
    {
    }
};
  • explicitを使用して、明示的に値渡しでコンストラクタを呼ばれないようにする
class A { public: A(int); };
class B { public: B(int); };

void f(const A&);
void f(const B&);

int main() {
f(3); // エラー:どっちを呼ぶか決定できない
return 0;
}

C++ クラス設計に関するノート www.ogis-ri.co.jp