Effective C++ 第三章 その2

項目11: 未定義private関数よりも =delete を優先する

  • C++ が必要に応じて自動的に作成する関数 = 特殊メンバ関数
    • その代表がコピーコンストラクタとコピー代入演算子
    • C++98 では private にすることで対応していた
      • この関数にアクセスしようとしても、未リンクでエラーになる
template<class charT, class traits = char_traits<charT>>
class basic_ios : public ios_base {
  public:
       ...
  private:
     basic_ios(const basic_ios &); // not defined
     basic_ios& operator=(const basic_ios&); // not defined
}
  • C++11 では delete を使える
    • delete した場合、どうやっても呼び出せないし、これらの関数を呼び出そうとするとコンパイル時に発見することができる
template<class charT, class traits = char_traints<charT>>
class basic_ios : public ios_base
{
   public:
      ...
      basic_ios(const basic_ios& ) = delete;
      basic_ios& operator=(const basic_ios&) = delete;
}
  • さらに delete はどんな関数でも適用できる
class Hoge
{
   bool isLucky(int number);
   bool isLucky(char number)=delete;
   bool isLucky(bool number)=delete;
   bool isLucky(double number)=delete;
}
template<typename T>
void f(T* ptr)

template<>
void f<char>(char*) = delete;

template<>
void f<void>(void*) = delete;
  • クラス内テンプレート関数に対して、テンプレートの特殊化を用いて無効なインスタンス化を防ぐ場合、C++98 の場合の private にする方法は実現できない。テンプレートの特殊化はクラススコープではなく、名前空間スコープで定義する必要があるため。
    • delete の場合名前区間スコープで定義することができる。

項目12: オーバーライドする関数は override と宣言する

  • 仮想関数のオーバライドは以下のように行う
class Base
{
public:
    virtual void doWork() {
    };
};

class Derived: public Base
{
public:
    virtual void doWork() {
        //
    };
};

void _override()
{
    std::unique_ptr<Base> upb = 
        std::make_unique<Derived>();
    upb->doWork();
}
  • オーバーライドを正しく動作するように宣言するのは難しい。
    • オーバーライドする条件をすべて満たさない限り、別のメンバー関数として定義される。
    • 以下のケースでは、Derived2 クラスのメンバ関数はすべて Base2 クラスのメンバ関数のオーバーライドではない
class Base2
{
public:
    virtual void mf1() const {};
    virtual void mf2(int x) {};
    virtual void mf3() & {};
    void mf4() const {};
};

class Derived2
{
public:
    virtual void mf1() {};
    virtual void mf2(unsigned int x) {};
    virtual void mf3() && {};
    void mf4() const {};
};
  • override とすると、コンパイル時にオーバーライドできないことを発見できる
class Base2
{
public:
    virtual void mf1() const {};
    virtual void mf2(int x) {};
    virtual void mf3() & {};
    virtual void mf4() const {};
};

class Derived2 : public Base2
{
public:
    virtual void mf1() const override {};
    virtual void mf2(int x) override {};
    virtual void mf3() & override {};
    virtual void mf4() const override {};
};
  • メンバ関数の参照修飾子を用いると、左辺値オブジェクト、右辺値オブジェクト(*this)を区別できる