関数の新機能

noexcept (C++11、 C++17)

C++11 で 追加 さ れ た noexcept キーワード を 使う と、 関数 が 例外 を 送出 する 可能性 が、 ある のか ない のかを 示す こと が でき ます。 この よう な 機能 を、 例外 仕様 と 呼び ます。

void f1() noexcept; // 例外 を 送出 し ない 
void f2(); // 例外 を 送出 する かも しれ ない 
void f1() noexcept {}
void f 2() {} 
int main() { 
 f1(); 
 f2(); 
}

また、 noexcept には bool 型 の オペランド を 指定 する こと も でき ます。 noexcept 自身 が “no” で 始まる 否定 形 なので、 ややこしい の です が、 true ならば 例外 を 送出 し ない、 false ならば 例外 を 送出 する 可能性 が ある こと を 表し ます。

void f3() noexcept(true); // 例外 を 送出 し ない 
void f4() noexcept(false); // 例外 を 送出 する かも しれ ない 
void f3() noexcept(true) {} 
void f4() noexcept(false) {} 
int main() {
 f3(); 
 f4(); 
}

移譲コンストラクタ (C++11)

C++11 には、 移譲 コンス トラクタ という 機能 が 追加 さ れ て おり、 コンス トラクタ から、 同じ クラス の 他 の コンス トラクタ を 呼び出せる よう になり まし た。

#include < iostream > 
class MyClass { 
 public:
 MyClass( int a) : MyClass( a, 0) { 
  std::cout << "MyClass( int)" << std::endl; 
 } 
 MyClass( int a, int b) : mA( a), mB( b) { 
  std::cout << "MyClass( int, int)" << std::endl; 
 } 
 private: 
  int mA, mB; 
}; 
int main() { 
 MyClass mc1( 10); 
 MyClass mc2( 1, 2); 
}

実行結果: MyClass( int, int) MyClass( int) MyClass( int, int)

継承コンストラクタ (C++11)

C++11 には、 基底 クラス の コンス トラクタ を 派生 クラス で そのまま 使える よう に する、 継承 コンス トラクタ の 機能 が 追加 さ れ て い ます。派生 クラス の 側 に「 using 基底 クラス 名:: コンス トラクタ 名;」 の よう な 形 で 記述 する と、 基底 クラス の すべて の コンス トラクタ が、 派生 クラス 側 でも 使える よう になり ます。

#include < iostream > 
class Base { 
 public: 
  Base( int a) { 
   std::cout << "Base( int)" << std::endl; 
  } 
  Base( const char* s) { 
   std::cout << "Base( const char*)" << std::endl; 
  } 
};
class Derived : public Base { 
 public: 
  using Base::Base; // 引数 が const char* の コンス トラクタ は、 派生 クラス で 定義 
  Derived( const char* s) : Base( s) { 
   std::cout << "Derived( const char*)" << std::endl; 
  } 
}; 
Derived d(" xyz"); // OK int 
main() {}

実行結果: Base( const char) Derived( const char)

override (C++11)

C ++ 11 では、 メンバ 関数 の オーバーライド を 行っ て いる こと を 明示 的 に 示す、 override という キーワード が 追加 さ れ て い ます。

#include < iostream > 
class Base { 
 public: virtual void SetData( int id) {
  std::cout << "Base (int)" << std::endl; 
 } 
 virtual void SetData( const char* name) { 
  std::cout << "Base (const char*)" << std::endl; 
 } 
}; 
class Derived : public Base { 
 public: struct Param {}; 
 void SetData( int id) override { 
  std:: cout << "Derived (int)" << std:: endl; 
 } 
 void SetData( const char* name) override { 
  std::cout << "Derived (const char*)" << std::endl; 
 }
 virtual void SetData( const Param& param) { 
  std::cout << "Derived (const Param&)" << std::endl; 
 } 
}; 
int main() { 
 Derived d; 
 Base* b = &d; 
 b->SetData( 10); 
 b->SetData("abc"); 
 Derived::Param param; 
 d.SetData(param); 
}

実行結果: Derived (int) Derived (const char*) Derived (const Param&)

override の 追加 を 行っ た こと、 新規 で 追加 し た 関数 にだけ virtual を 付ける こと によって、 どの 関数 が オーバーライド さ れ た もの で、 どの 関数 が そう で ない もの かを 明確 に 区別 できる よう になり まし た。 安全 性 も 高まっ て い ます。

関数の削除 (C++11)

C ++ 11 で、 関数 の 定義 を 削除 する 機能 が 追加 さ れ まし た。 void f() = delete; この よう に 関数 宣言 の 末尾 に「= delete」 を 置く と、 その 関数 は 削除 さ れ ます。 削除 さ れ た 関数 を 呼び出し たり、 アドレス を 取得 し たり する コード を 書く と、 コンパイル エラー になり ます。「= delete」 の 代表的 な 利用 例 として、 オブジェクト の コピー を 禁止 する こと が 挙げ られ ます。

before (C++98/ 03)

C++03 以前 は、 コピーコンストラクタ や コピー 代入 演算子 を private に する こと によって、コピー を 禁止 し て い まし た。

class MyClass { 
 public: 
  MyClass() {} 
 private: 
  MyClass( const MyClass&); 
  MyClass& operator =( const MyClass&); 
}; 
int main() { 
 MyClass a, b; 
 MyClass c(a); // コンパイル エラー 
 a = b; // コンパイル エラー
}

この 方法 の 場合、 private に する だけで なく、 定義 も 書か ない こと が 重要 です。 定義 が ある と、 MyClass の メンバ 関数 からは 呼び出せ て しまい ます。 定義 を 書か なけれ ば、 呼び出そ う と し た とき に リンク エラー に でき ます。

after (C++11/ 14/ 17)
class MyClass { 
 public: 
  MyClass() {} 
  MyClass( const MyClass&) = delete; 
  MyClass& operator =( const MyClass&) = delete; 
}; 
int main() { 
 MyClass a, b; 
 MyClass c(a); // コンパイル エラー 
 a = b; // コンパイル エラー 
}

こちら の 方 が 簡単 で 明確 です し、 C ++ 03 以前 の 方法 の よう に、 MyClass の 他 の メンバ 関数 からの 呼び出し に 注意 する 必要 も 無くなり ます。 削除 さ れ て いる 訳 です から、 friend の 利用 を 含め、 どんな 手段 を 使っ た として も 絶対 に 呼び出せ ませ ん。なお、 before の サンプル プログラム と 違っ て、 コピーコンストラクタ と コピー 代入 演算子 の 宣言 を、 public に 変更 し て い ます。 これ は 必須 という 訳 では あり ませ ん が、 public に し て おく こと で、 誤っ て これら の 関数 を 使お う と し た とき に、「 削除 さ れ て いる」 という 主旨 の コンパイル エラー を 出す こと が でき ます。 private に なっ て いる と、「 非公開 な 関数 に アクセス でき ない」 という 主旨 の エラー で 報告 さ れる 可能性 が あり、 関数 が 削除 さ れ て いる という 事実 が 伝わり づらく なり ます。

コメント

タイトルとURLをコピーしました