C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++のクラス内部構造を解説!デフォルトコンストラクタの仕組みと動作

はじめに:クラスの内部構造とデフォルトコンストラクタ

本記事では、C++におけるクラスの内部構造(internals)について解説します。その前提として、内部構造と密接に関わる「デフォルトコンストラクタ」の仕組みから確認していきましょう。

デフォルトコンストラクタとは、引数を一切取らないコンストラクタのことです。ユーザーが定義したものも、コンパイラが自動生成したものも含まれます。では、なぜデフォルトコンストラクタが必要なのでしょうか。

コンパイラがデフォルトコンストラクタを挿入するケース

デフォルトコンストラクタが定義されていない場合、コンパイラは暗黙的にそれを宣言します。デフォルトコンストラクタはクラスの内部要素を初期化するために利用されますが、クラスのデータメンバー(int型など)は初期化されない点に注意が必要です。

コンパイラがデフォルトコンストラクタのコードを挿入する代表的な状況は次の2つです。

  • クラスが、デフォルトコンストラクタを持つ別のクラスから派生している場合
  • クラスが、デフォルトコンストラクタを持つ他のクラス型のオブジェクトをメンバーとして保持している場合

いずれの場合も、コンパイラは基底クラスやメンバーオブジェクトのデフォルトコンストラクタを呼び出すコードを、現在のオブジェクト向けに自動的に挿入します。実際のコードで挙動を確かめてみましょう。

サンプルコード

#include<iostream>
using namespace std;
class Base {
    public:
        // コンパイラがクラス"Base"用のコンストラクタを自動生成する
};
class ClassA {
    public:
    ClassA(){
        cout << "ClassA Constructor defined by user" << endl;
    }
    int x; // 自動的には初期化されない
};
class ClassB : public ClassA {
    // コンパイラがClassBのコンストラクタを生成し、
    // 基底クラスClassAのコンストラクタを呼び出すコードを追加する
};
class ClassC : public ClassA {
    public:
        ClassC() { // ユーザー定義のコンストラクタだが、コンパイラが
                   // ClassAのコンストラクタ呼び出しコードを追加する
            cout << "User defined ClassC Constructor" << endl;
    }
};
class ClassD {
    public:
        ClassD(){
            // ユーザー定義のデフォルトコンストラクタ。
            // コンパイラがメンバーobjに対するClassAの
            // コンストラクタ呼び出しコードを追加する
            cout << "User-defined constructor for ClassD" << endl;
    }
    private:
        ClassA obj;
};
int main() {
    Base baseObj;
    ClassB b;
    ClassC c;
    ClassD d;
}

実行結果

ClassA Constructor defined by user
ClassA Constructor defined by user
User defined ClassC Constructor
ClassA Constructor defined by user
User-defined constructor for ClassD

出力結果から読み取れるポイント

  • ClassB:コンストラクタを明示的に定義していなくても、暗黙的に生成されたコンストラクタが先に基底クラスClassAのコンストラクタを呼び出します。
  • ClassC:ユーザー定義のコンストラクタがある場合でも、その処理が実行されるにClassAのコンストラクタが呼び出されています。
  • ClassD:メンバーオブジェクトobjも、ClassD自身のコンストラクタ本体よりも先に初期化されていることが分かります。

このようにC++では、開発者が明示的に記述しなくても、コンパイラが必要に応じてコンストラクタの呼び出しコードを裏側で挿入することで、オブジェクトの整合性が保たれる仕組みになっています。

  1. 【C++】コンストラクターにvector(ベクトル)を渡す方法をわかりやすく解説

    C++では、std::vectorをクラスのコンストラクターに渡すことで、オブジェクト生成時にまとめてデータを初期化できます。この記事では、ベクトルをコンストラクターに渡すシンプルなC++プログラムを、アルゴリズム・サンプルコード・実行結果とともに紹介します。アルゴリズム処理の流れは以下の通りです。開始   Vectorという名前のクラスを宣言する     vector<int>型のメンバー変数vecを宣言する     Vectorクラスのコンストラクターを宣言する       ベクトルオブジェクトvを引数として受け取る       vec = v として初期化する     ベクト

  2. C++のローカルクラスとは?特徴・制約と実装例をわかりやすく解説

    ローカルクラスとは C++において、関数の中で宣言されたクラスは「ローカルクラス」と呼ばれます。これは、そのクラスが定義された関数の内部でのみ有効であるためです。 まず、ローカルクラスの基本的な構造を見てみましょう。 #include<iostream> using namespace std; void func() { class LocalClass { }; } int main() { return 0; } 上記の例では、関数 func() の中で LocalClass が定義されています。このように関数の内部で宣言されたクラスが、ローカルクラス