C++ の型システム

C++ では 、 型の 概念が非常に重要です。 変数、関数の引数、関数の戻り値をコンパイルするには、それぞれに型が必要です。 さらに、すべての式 (リテラル値を含む) には、評価前にコンパイラーにより暗黙的に型が指定されます。 型の例としては、整数値の格納、浮動小数点値 (スカラー データ型とも呼ばれる) の格納、テキストを格納する標準ライブラリ クラス intdoubledoubleintがあります。 または を定義することで、独自の型を作成 class できます struct 。 型は、変数 (または式の結果) に割り当てられるメモリの量、その変数に格納される値の種類、それらの値の解釈方法 (ビット パターンとして)、その型で実行可能な操作を指定します。 ここでは、C++ の型システムの主な機能の概要を示します。

用語

変数: 定義されているコードのスコープ全体で参照するデータにアクセスするために名前を使用できるよう、データの数量のシンボリック名。 C++ では、 通常、変数 はスカラー データ型のインスタンスを参照するために使用されます。一方、他の型のインスタンスは通常、オブジェクト と呼 ばれるのに対して です

オブジェクト: わかりやすくするために、この記事では オブジェクトという用語を使用してクラスまたは構造体のインスタンスを参照します。一般的な意味で使用される場合は、すべての型 (スカラー変数も含む) が含まれます。

POD 型 (プレーンな古いデータ): C++ のデータ型の非公式カテゴリは、スカラー型 (「基本型」セクションを参照) または POD クラス である型 を参照します。 POD クラスには、POD ではない静的データ メンバーが含まれており、ユーザー定義コンストラクター、ユーザー定義デストラクター、またはユーザー定義代入演算子 ’ はありません。 また、POD クラスに仮想関数、基底クラス、プライベートまたは保護された非静的データ メンバーもありません。 POD 型は、外部データ交換によく使用されます。たとえば、C 言語で記述されたモジュール (POD 型しかありません) との交換などです。

変数と関数の型の指定

C++ は 、強力に型指定された言語 であり、静的 に型指定されます。すべてのオブジェクトには 型が含まれており、その型は変更されません (静的データ オブジェクトと混同しないでください)。 コードで変数を宣言する場合は、その型を明示的に指定するか、 キーワードを使用して、初期化子から型をデ auto duceするようにコンパイラに指示する必要があります。 コードで関数を宣言する場合は、各引数の型とその戻り値を指定するか、関数によって値が返されていない void 場合は指定する必要があります。 例外は、任意の型の引数を使用できる関数テンプレートを使用する場合です。

最初に変数を宣言すると、後で型を変更することはできません。 ただし、変数の値または関数の戻り値を別の型の別 ’’ の変数にコピーできます。 このような操作は型変換と ばれるものになります。これは必要な場合がありますが、データ損失や不正の原因の可能性があります。

POD 型の変数を宣言するときは、初期化する (つまり、初期値を指定する) ことを強くお勧めします。 変数を初期化しないと、以前そのメモリ位置にたまたま存在していたビットで構成される "不要な" 値が含まれたままになります。 これは、特に自動的に初期化が行われる別の言語を使用していた場合は、覚えておくべき C++ の重要な側面です。 非 POD クラス型の変数を宣言した場合、コンストラクターにより初期化が実行されます。

次の例は、それぞれ記述を含む、いくつかの簡単な変数宣言を示しています。 この例は、コンパイラが型情報を使用して、特定の後続の処理を許可または拒否する方法も示しています。

int result = 0;              // Declare and initialize an integer.
double coefficient = 10.8;   // Declare and initialize a floating
                             // point value.
auto name = "Lady G.";       // Declare a variable and let compiler
                             // deduce the type.
auto address;                // error. Compiler cannot deduce a type
                             // without an intializing value.
age = 12;                    // error. Variable declaration must
                             // specify a type or use auto!
result = "Kenny G.";         // error. Can’t assign text to an int.
string result = "zero";      // error. Can’t redefine a variable with
                             // new type.
int maxValue;                // Not recommended! maxValue contains
                             // garbage bits until it is initialized.

基本 (組み込み) 型

一部の言語とは異なり、C++ には他のすべての型の派生元となる汎用基本型はありません。 この言語には、組 み込み型 とも呼ばれる多くの基本 型 が含まれています。 これには、、 などの数値型に加えて、ASCII 文字と UNICODE 文字の 型と 型 intdoublelongboolcharwchar_t が含まれます。 ほとんどの整数の基本型 (、、、および関連する型を除く) には、変数に格納できる値の範囲を変更するバージョン booldoublewchar_tunsigned があります。 たとえば、32 ビット符号付き整数を格納する は int 、-2,147,483,648 から 2,147,483,647 の値を表します。 も 32 ビットとして格納される は unsigned int 、0 から 4,294,967,295 の値を格納できます。 各ケースで格納できる値の合計数は同じです。範囲のみ異なります。

基本型は、実行可能な操作や他の基本型に変換する方法を制御する組み込みの規則を持つコンパイラにより認識されます。 組み込み型とそのサイズと数値の制限の完全な一覧については、「組み込み型」 を参照してください

次の図は、Microsoft C++ 実装の組み込み型の相対サイズを示しています。

Diagram of the relative size in bytes of several built in types.

次の表に、Microsoft C++ 実装で最もよく使用される基本型とそのサイズを示します。

Type サイズ 解説
int 4 バイト 整数値の既定のオプション。
double 8 バイト 浮動小数点値の既定のオプション。
bool 1 バイト true または false になる値を表します。
char 1 バイト 以前の C スタイル文字列内の ASCII 文字や、UNICODE に変換する必要がない std::string オブジェクトの ASCII 文字に使用します。
wchar_t 2 バイト UNICODE 形式でエンコードできるワイド文字を表します (Windows では UTF-16。他のオペレーティング システムでは異なる場合があります)。 これは、型 std::wstring の文字列で使用される文字型です。
unsigned char 1 バイト C++ には組み込みのバイト型はありません。 バイト unsigned char 値を表す場合は、 を使用します。
unsigned int 4 バイト ビット フラグの既定のオプション。
long long 8 バイト 非常に大きな整数値を表します。

その他の C++ 実装では、特定の数値型に異なるサイズを使用できます。 C++ 標準で必要なサイズとサイズの関係の詳細については、「組み込み型」 を参照してください

void 型

型は特殊な型です。型の変数を宣言することはできませんが、生 (型指定されていない) メモリを割り当てるときに必要な場合がある 型の変数 (へのポインター) を宣言 voidvoidvoid *void できます。 ただし、 へのポインターはタイプ セーフではなく、一般に最新の C++ では使用 void を強く推奨されません。 関数宣言では、戻り値は関数が値を返しません。これは、 の一般的で許容 void される使用です void 。 たとえば、パラメーター リストで宣言するパラメーターが 0 の C 言語の必須関数ですが、この方法は最新の C++ では推奨されません。また、 を宣言 voidfou(void) する必要があります fou() 。 詳細については、「型変換」と 「タイプ 安全性」を参照してください

const 型修飾子

組み込み型またはユーザー定義型は、const キーワードで修飾することができます。 さらに、メンバー関数は const 、-修飾され、さらに const -オーバーロードされる場合があります。 型の値 const は、初期化後に変更できません。


const double PI = 3.1415;
PI = .75 //Error. Cannot modify const variable.

修飾子は関数と変数の宣言で広く使用され const 、"const correctness" は C++ の重要な概念です。基本的には、 を使用して、コンパイル時に値が意図せずに変更されないという保証を行います。 const 詳細については、 const を参照してください。

const は非 const バージョンとは異なります。たとえば、 は const int とは異なる型です int 。 変数から const_castconst_cast を削除する必要があるまれなケースで、C++ 演算子を使用できます。 詳細については、「型変換」と 「タイプ 安全性」を参照してください

文字列型

厳密に言えば、C++ 言語には組み込みの文字列型はありません。および は 1 文字を格納します。文字列を近似するためにこれらの型の配列を宣言し、終端の null 値 (ASCII など) を、最後の有効な文字 (C スタイルの文字列とも呼ばれる) の 1 つ後の配列要素に追加する必要 charwchar_t'\0'char。 C スタイル文字列では、かなり多くのコードを記述するか、外部文字列ユーティリティ ライブラリ関数を使用する必要がありました。 ただし、最新の C++ では、標準ライブラリの型 (8 ビットの -type 文字列の場合) または std::stringcharstd::wstring (16 ビット wchar_t の -type 文字列の場合) があります。 これらの C++ 標準ライブラリ コンテナーは、準拠する C++ ビルド環境に含まれる標準ライブラリの一部なので、ネイティブ文字列型と考えられます。 #include <string> ディレクティブを使用するだけで、これらの型をプログラムで使用できるようになります (MFC または ATL を使用している場合は、 クラスも使用できますが CString 、C++ 標準の一部ではありません)。 null で終わる文字配列 (前述の C スタイル文字列) は、最新の C++ では使用しないことを強くお勧めします。

ユーザー定義データ型

、、、または を定義する場合、そのコンストラクトは、基本的な型である場合と同様に、コードの残りの classstructunionenum 部分で使用されます。 メモリ内には既知のサイズがあり、コンパイル時のチェックを要求し、実行時にプログラムの有効期間を問い合わせるために使用する方法に関する一定の規則もあります。 基本の組み込み型とユーザー定義型の主な相違点は次のとおりです。

  • コンパイラには、ユーザー定義型に関する組み込みの情報はありません。 コンパイル プロセス中に定義が検出された場合に型を学習します。

  • クラス メンバーまたは非メンバー関数として適切な演算子を定義することで (オーバーロードを通じて)、型で実行可能な操作と他の方に変換する方法を指定します。 詳細については、「関数のオーバーロード 」を参照してください。

ポインター型

C 言語の初期バージョンからそうであったように、C++ では特別な宣言子 * (アスタリスク) を使用して、ポインター型の変数を宣言できます。 ポインター型には、実際のデータ値が格納されているメモリ位置のアドレスが格納されます。 最新の C++ では、これらは生ポインターと呼ばされ、特殊な演算子 (アスタリスク) または (ダッシュとより大きい) を使用してコード内で -> アクセスされます。 これは逆参照と呼ばれるので、どのポインターを使用するかは、スカラーへのポインターを逆参照するか、オブジェクト内のメンバーへのポインターを逆参照するかによって異なります。 ポインター型の使用は、長い間 C および C++ プログラム開発における最も困難で複雑な側面の 1 つでした。 このセクションでは、必要に応じて生のポインターを使用するのに役立ついくつかの事実とプラクティスについて説明しますが、最新の C++ では、スマート ポインターの進化により、オブジェクトの所有権に生ポインターを使用する必要がなくなりました (または推奨される)。 ’’ 現在でも、オブジェクトの観察には生のポインターが役立ち、使用してもかまいませんが、オブジェクトの所有権に使用する必要がある場合は慎重に使用し、生のポインターが所有するオブジェクトを作成および破棄する方法について十分に考慮してください。

まず知る必要がある点は、生のポインター変数を宣言すると、ポインターが逆参照されるときに参照するメモリ位置のアドレスを格納するのに必要なメモリだけが割り当てられるという点です。 データ値自体 (バッキング ストアとも呼 ばれる)のメモリの割り当てはまだ割り当てられません。 言い換えると、生のポインター変数を宣言することで、実際のデータの変数ではなくメモリ アドレスの変数を作成することになります。 バッキング ストアへの有効なアドレスが含まれることを確認する前にポインター変数を逆参照すると、プログラムで定義されていない動作 (通常は重大なエラー) が発生します。 この種のエラーの例を次に示します。

int* pNumber;       // Declare a pointer-to-int variable.
*pNumber = 10;      // error. Although this may compile, it is
                    // a serious error. We are dereferencing an
                    // uninitialized pointer variable with no
                    // allocated memory to point to.

この例では、実際の整数データまたはそこに割り当てられた有効なメモリ アドレスを格納するメモリを割り当てずに、ポインター型を逆参照しています。 このエラーを修正するコード例を次に示します。

    int number = 10;          // Declare and initialize a local integer
                              // variable for data backing store.
    int* pNumber = &number;   // Declare and initialize a local integer
                              // pointer variable to a valid memory
                              // address to that backing store.
...
    *pNumber = 41;            // Dereference and store a new value in
                              // the memory pointed to by
                              // pNumber, the integer variable called
                              // "number". Note "number" was changed, not
                              // "pNumber".

修正後のコード例では、ローカル スタック メモリを使用して、pNumber がポイントするバッキング ストアを作成します。 ここでは、説明を簡単にするために基本型を使用しています。 実際には、ポインターのバッキング ストアは、多くの場合、キーワード式 (C スタイルのプログラミングでは、以前の C ランタイム ライブラリ関数が使用されました) を使用して、ヒープ(またはフリーストア)と呼ばれるメモリ領域に動的に割り当てられるユーザー定義型です。 割り当てられると、これらの変数は、特にクラス定義に基づく場合はオブジェクトと呼ばれます。 で割り当てられたメモリは、対応するステートメントによって削除する必要があります (または、関数を使用して割り当てる場合は newdeletemalloc() 、C ランタイム関数 free() )。

ただし、動的に割り当てられたオブジェクト (特に複雑なコードでは) を削除することを忘れがちです。この場合、メモリ リーク と呼ばれるリソースのバグ が発生します。 したがって、最新の C++ では生のポインターを使用しないことを強くお勧めします。 ほとんどの場合、生のポインターをスマート ポインター にラップする方が良いです。これは、デストラクターが呼び出されると (コードがスマート ポインターのスコープ外に出た場合に) メモリを自動的に解放します。スマート ポインターを使用すると、C++ プログラムのバグのクラス全体を事実上排除できます。 次の例では、MyClass がパブリック メソッド DoSomeWork(); を持つユーザー定義型であることを前提としています。

void someFunction() {
    unique_ptr<MyClass> pMc(new MyClass);
    pMc->DoSomeWork();
}
  // No memory leak. Out-of-scope automatically calls the destructor
  // for the unique_ptr, freeing the resource.

スマート ポインターの詳細については、「スマート ポインター」 を参照してください

ポインター変換の詳細については、「型変換」および「 型の安全性」を参照してください

ポインター全般の詳細については、「ポインター」 を参照してください

Windows のデータ型

C および C++ 用の従来の Win32 プログラミングでは、ほとんどの関数で Windows 固有の typedef とマクロ (で定義) を使用して、パラメーターの型と戻り値 #definewindef.h を指定します。 これらのWindowsデータ型は、主に C/C++ 組み込み型に指定された特殊な名前 (別名) にすすむものです。 これらの typedef とプリプロセッサ定義の完全な一覧については、「Windowsを参照してください。 や など、これらの typedef の一部 HRESULTLCID は便利でわかりやすいものがあります。 など、その他には特別な意味はありません。これは、基本的な INT C++ 型のエイリアスにすすまれます。 他の Windows のデータ型には、C プログラミングおよび 16 ビット プロセッサの時代から残っている名前がありますが、最新のハードウェアやオペレーティング システムでは目的も意味もありません。 また、ランタイム ライブラリに関連付けられている特殊なデータWindows、ランタイム の基本データ型 としてWindows一覧表示されます。 最新の C++ では、値の解釈方法について Windows の型が追加の意味を伝えるのでない限り、一般的なガイドラインとして C++ の基本型が推奨されます。

詳細情報

C++ の型システムの詳細については、次のトピックを参照してください。

値型
値型 とその使用 に関する問題について説明します。

型変換とタイプ 安全性
よくある型変換の問題について説明し、その回避方法を示します。

こちらもご覧ください

C++ へようこそ
C++ 言語リファレンス
C++ 標準ライブラリ