ホームに戻る
出典 :
関連 :
目次 :
C++11 (ISO/IEC 14882:2011) とは
2011年に定められたC++のISO標準規格。以前の規格であるC++03に対し、コア言語、標準ライブラリの双方に大規模な修正・拡張が施されている。
後継規格としてC++14、C++17、C++20が存在するが、これらはC++11に対するマイナーアップデートに位置づけられる。
ここではC++11で追加された機能の一部を紹介する。C++11(以降)での拡張は多岐にわたるため、詳細は出典元を参照のこと。
ヌルポインタ ( nullptr )
C++03まではCと同様、定数 0 に整数定数とヌルポインタという二つの役割が与えられている。
定数 0 の代わりに NULL を用いることで、この潜在的な曖昧性をある程度回避できてはいたが、C++では新たな問題が発生していた。
Cにおける NULL はプリプロセッサマクロとして、( (void*) 0) または 0 と展開されるが、
C++ではvoid* 型から他のポインタ型への暗黙的変換が認められないため、NULL を同様に定義すると char* c = NULL; のような表現はコンパイルエラーとなる。
このような経緯からC++では NULL はあらゆるポインタ型への変換が例外的に認められる 0 と展開される。
しかし以下のように関数オーバーロードと組み合わせることで回避しにくい問題となる。
void foo(char*);
void foo(int);
:
foo(NULL); //< ポインタ型のつもりでNULLを与えているが…
上記のコード例ではNULL が 0 に置換されるため、foo(int) が呼ばれることとなる。多くの場合で、これはプログラマの意図に反している。
この問題を受け、C++11ではキーワード nullptr が導入された。
nullptr は整数型との比較・代入ができないが、あらゆるポインタ型との比較・代入ができる 。
void foo(char*);
void foo(int);
:
foo(nullptr); //< nullptr はポインタ型と解釈されるため、foo(char*) が呼ばれる
常に同じ結果を返し、副作用の無い(スコープ外の変数やメモリ等を操作しない)式を定数式と呼ぶ。(3 + 4 など定数同士の演算、または単項の定数)
定数式はコンパイラの最適化の対象となり、多くの場合でコンパイル時に演算が行われ、プログラム中にはその結果のみが格納される。
通常、関数呼び出しやオブジェクトコンストラクタが出現すると定数式とは見なされないが、
C++11ではキーワード constexpr が導入され、関数やオブジェクトコンストラクタが定数式であることを保証できるようになった。
この constexpr は const の代替として、定数定義時に用いることも可能である。
詳細はリンク先を参照。
初期化子リスト
C++ではCと同様、構造体を初期化する際に、初期値をブレース { } 内に列記(初期化子リスト)することで各メンバに割り当てることができる。これは入れ子となった構造体や、構造体配列でも有効である。
struct St_Object
{
float first;
int second;
};
St_Object scalar = { 0.43F, 10 }; //< St_Object 型変数の初期化
St_Object anArray[] = { {13.4F, 3}, {43.28F, 29}, {5.934F, 17} }; //< St_Object 型配列変数の初期化
C++11では初期化子リストがテンプレート std::initializer_list と結び付けられ、コンストラクタなどの関数が初期化子リストを引数として取ることができるようになった。
class CSequenceClass
{
public:
CSequenceClass(std::initializer_list<int> list);
};
// 初期化子リストを用いた CSequenceClass 型変数の初期化
CSequenceClass someVar = { 1, 4, 5, 6 };
// ↑は↓と等価である
std::initializer_list<int> i_er = { 1, 4, 5, 6 };
CSequenceClass someVar(i_er);
C++03までは、型の初期化の方法が複数存在する場合、取り違えると同じ結果にはならなかった。
C++11では「統一初期化記法(uniform initialization)」が導入され、どのようなオブジェクトでも同様の記法で初期化できるようになった。
これは上記の初期化子リスト構文の拡張である。詳細はリンク先を参照。
メンバ変数を宣言時に初期化することが可能。コンストラクタの記述を省略できる。詳細はリンク先を参照。
C++03(およびC)では変数の型は明示的に指定せねばならず、テンプレートやメタプログラミングとの相性が良くなかった。
C++11では型推論が導入され、この制約が軽減されている。詳細はリンク先を参照。
C#やJavaの foreach と同様、for 文の別構文により、コンテナを簡単に走査することができる。詳細はリンク先を参照。
名前を付けずに定義された関数を無名関数と呼ぶ。
C++11ではラムダ式を用いて無名関数を定義し、無名関数を関数の引数として渡すことができるようになった。詳細はリンク先参照。
C++03までは、コンストラクタは他のコンストラクタを呼び出せず、それぞれのコンストラクタでメンバの初期化を行う必要があったため、コードの重複が避けられなかった。
また、基底クラスのコンストラクタは派生クラスには直接公開されず、派生クラス側で(基底クラスと重複する)コンストラクタを定義する必要があった。
C++11ではコンストラクタが他の同等なコンストラクタを呼び出すこと(委譲)が可能となっている。詳細はリンク先参照。
C++03までの列挙型 (enum) は型安全ではなく、列挙値は暗黙的に整数型への変換が可能であった。
また、同一スコープに存在する二つの異なる列挙体が、同じ名前のメンバを持つことができなかった。
C++11では型安全な(クラス化された)列挙体 (enum class) が導入された。詳細はリンク先参照。
C++03までは、基底クラスの仮想関数をオーバーライドする際に、(タイプミスなどで)意図に反する新しい仮想関数を作成してしまうことがある。
C++11では override キーワードにより解決を図ることができる。詳細はリンク先参照。
継承・オーバーライドの禁止 ( final )
C++11では final キーワードが追加された。クラスに適用した場合と仮想関数に適用した場合で、意味が異なる。
クラスの final 修飾
クラスを final 修飾した場合、 そのクラスを派生(継承)させることができなくなる 。
// 基底クラス
// final : 継承不可となる
class C_Base final
{
public:
virtual void some_func(float);
virtual int f();
};
// 派生クラス
// エラー : 継承できない基底クラスを継承しようとしている
class C_Derived : C_Base
{
:
};
仮想関数の final 修飾
仮想関数を final 修飾した場合、 その関数はオーバーライド不可となる 。
// 基底クラス
class C_Base
{
public:
virtual void some_func(float) final; //< final : some_func(float) はオーバーライド不可となる
};
// 派生クラス
class C_Derived : C_Base
{
void some_func(float); //< 不正 : final 修飾された関数をオーバーライドしようとしている
};
C++03(およびC)では、既存の型に別名を付ける際には typedef を用いていた。しかし typedef にはテンプレートを作ることはできないという制約がある。
C++11では using を用いることで、テンプレートに対しても別名を付与することが可能である。詳細はリンク先を参照。
動的メモリ(ヒープ)を安全に使用するための機構。
スマートポインタを使用すると、スコープを抜ける際に自動的にヒープが解放されるため、解放忘れによるメモリリークの危険性が減る。詳細はリンク先を参照。
複数個の値をまとめて管理できるデータ型。構造体やクラスと異なり事前の型定義が必要なく、簡便に用いることができる。詳細はリンク先を参照。
クラスを定義すると、以下のメンバ関数が暗黙的に定義される。
- デフォルトコンストラクタ (引数無しのコンストラクタ)
- コピーコンストラクタ
- ムーブコンストラクタ
- コピー代入演算子
- ムーブ代入演算子
- デストラクタ
delete / default を用いることで、これらの暗黙的に定義される特殊メンバ関数の定義を制御することができる。詳細はリンク先を参照。
あるオブジェクトから、別のオブジェクトにリソース(所有権)を受け渡すことができる。コピーするよりもはるかに高速に処理できるため、特定の局面では有効である。
詳細はリンク先を参照。