C++のデフォルトコンストラクタとは、引数なしで呼び出せるコンストラクタのことです。
一般的には「引数を持たないコンストラクタ」と理解されることが多いですが、厳密にはすべての引数にデフォルト値が設定されていて、引数なしで呼び出せるコンストラクタも含まれます。
オブジェクトを生成するときに何も引数を渡さずに作れる仕組みであり、C++のクラス設計において基本となる要素のひとつです。
どのような場面で使われるのか
デフォルトコンストラクタは、単に引数なしでオブジェクトを作るときだけに関係するものではありません。
たとえば、配列として複数のオブジェクトをまとめて生成するときや、クラスのメンバとして別のオブジェクトを保持しているとき、さらに標準ライブラリのコンテナが要素を自動生成するときにも関わってきます。
そのため、デフォルトコンストラクタの有無は、クラスの使いやすさや他の機能との相性にも影響します。
自動で用意されるケース
C++では、クラスにコンストラクタをひとつも定義していない場合、コンパイラが自動的にデフォルトコンストラクタを宣言することがあります。
この仕組みによって、何も書かなくても引数なしでオブジェクトを作れる場合があります。
ただし、ここで注意したいのは、デフォルトコンストラクタが存在することと、メンバ変数が適切な値に初期化されることは別だという点です。
自動でデフォルトコンストラクタが用意されても、組み込み型のメンバ変数は未初期化のままになることがあります。
デフォルトコンストラクタがあっても安全とは限らない
デフォルトコンストラクタがあるからといって、オブジェクトの中身が必ず安全な状態になるとは限りません。
この点は、C++を学び始めたときにつまずきやすいポイントです。
特に、整数や浮動小数点数のような組み込み型のメンバは、明示的に初期化しなければ不定の値を持つことがあります。
つまり、「引数なしで生成できる」ことと「正しい初期状態で生成される」ことは同じではありません。
クラスを設計するときは、生成直後から意味のある状態になっているかどうかを意識することが大切です。
初期化はメンバ初期化リストが基本
C++では、メンバ変数の初期化はコンストラクタ本体の中で代入するよりも、メンバ初期化リストで行うのが基本です。
これは書き方の好みの問題ではなく、言語仕様上も重要な違いがあります。
コンストラクタ本体が実行される時点では、メンバの初期化自体はすでに行われています。
そのため、本体の中で値を設定する処理は、厳密には「初期化」ではなく「初期化後の代入」です。
この違いは組み込み型では目立ちにくいものの、クラス型のメンバ、const メンバ、参照メンバでは非常に重要になります。
無駄な処理を避けるためにも、また意図した初期状態を正しく表現するためにも、初期化リストを使う設計が基本になります。
引数付きコンストラクタを定義するとどうなるか
コンストラクタについて理解するうえで、とても重要なルールがあります。
それは、引数付きコンストラクタをひとつでも自分で定義すると、コンパイラは自動でデフォルトコンストラクタを用意しなくなるということです。
このため、引数ありの生成だけでなく引数なしの生成も許可したい場合は、デフォルトコンストラクタを明示的に定義しなければなりません。
この仕様を知らないままクラスを設計すると、「引数なしでも作れると思っていたのに作れない」という状況が発生しやすくなります。
= default の役割
デフォルトコンストラクタを明示的に残したいときに使われるのが = default です。
これは、コンパイラによる通常のデフォルトコンストラクタ生成をそのまま利用する意思を示す書き方です。
特別な処理は必要ないけれど、引数なしで生成できることを明確にしたい場合に適しています。
また、引数付きコンストラクタを定義しているクラスで、デフォルトコンストラクタも利用可能にしたいときにもよく使われます。
設計の意図がコード上ではっきり伝わるため、可読性の面でもメリットがあります。
= delete で生成を禁止する考え方
一方で、引数なしでオブジェクトを作らせたくない場合もあります。
そのような場面では = delete を使って、デフォルトコンストラクタを禁止することができます。
これは、必須の情報が与えられない限り有効な状態を作れないクラスに向いています。
たとえば、IDや設定値が必須のオブジェクトでは、引数なし生成を許してしまうと不完全な状態のインスタンスが生まれるおそれがあります。
そのため、デフォルトコンストラクタをあえて使えなくする設計は、クラスの安全性を高める手段としてよく用いられます。
自動生成できないケースに注意
デフォルトコンストラクタは、明示的に許可すれば必ず使えるわけではありません。
クラスの中に、デフォルト構築できないメンバが含まれている場合、そのクラス自身のデフォルトコンストラクタも使えなくなることがあります。
理由は単純で、引数なしで外側のクラスを作ろうとしても、内部メンバをどのように生成すればよいのか決められないからです。
このような場合は、クラス側で適切な初期値や生成方法を与える必要があります。
見た目ではシンプルなクラスでも、内部構造によってデフォルト構築の可否が変わる点は押さえておきたいところです。
constメンバと参照メンバは特に重要
const メンバや参照メンバを持つクラスでは、デフォルトコンストラクタの設計により慎重さが求められます。
これらは通常のメンバのように後から自由に代入できないため、オブジェクト生成時点で正しく初期化されていなければなりません。
そのため、コンストラクタ本体であとから値を入れるのではなく、最初から適切な値を与える必要があります。
この性質があるため、const や参照を持つクラスでは、メンバ初期化リストの重要性がさらに高まります。
初期化の順番は見た目どおりではない
C++では、メンバの初期化順序はコンストラクタの初期化リストに書いた順番ではなく、クラス内で宣言された順番で決まります。
この仕様を知らないと、見た目では正しそうに見えるのに実際の初期化順序が異なり、不具合や警告の原因になることがあります。
そのため、初期化リストを書くときは、クラス内の宣言順と同じ順番にそろえるのが基本です。
細かいルールに見えるかもしれませんが、クラス設計の信頼性に直結する部分です。
デフォルト初期化と値初期化の違い
C++では、オブジェクトの作り方によって初期化の意味が変わります。
ここで特に重要なのが、デフォルト初期化と値初期化の違いです。
引数なしでそのままオブジェクトを作る形は、通常はデフォルト初期化です。
一方で、空の波かっこを使って生成する形は値初期化になります。
組み込み型ではこの差が大きく、値初期化ではゼロ初期化されます。
ただし、クラス型になると話は少し複雑です。
単純に「波かっこを使えばすべてゼロになる」と考えるのは危険で、デフォルトコンストラクタの定義のされ方によっては、期待どおりの初期化にならない場合もあります。
このあたりはC++特有のわかりにくい部分ですが、初期化方法の違いが結果に影響するという点は理解しておきたいところです。
デフォルトメンバ初期化子との関係
C++11以降では、メンバ変数の宣言と同時に初期値を書くことができます。
これはデフォルトメンバ初期化子と呼ばれ、シンプルな初期値設定に非常に便利です。
この方法を使うと、コンストラクタ側でそのメンバを明示的に初期化しなかった場合に、宣言時の初期値が使われます。
一方で、コンストラクタのメンバ初期化リストで別の値を与えた場合は、そちらが優先されます。
初期化ルールを整理しやすくなるため、現在のC++では非常に実用的な書き方として広く使われています。
設計の観点で考えるべきこと
デフォルトコンストラクタを用意するかどうかは、単なる文法の問題ではありません。
クラスをどのように使わせたいか、生成直後にどのような状態であるべきかという設計思想に関わります。
引数なしで気軽に作れることを優先するのか、必須情報がなければ生成させない安全性を優先するのかによって、選ぶべき設計は変わります。
また、単に生成できるようにするだけでなく、そのときの状態が妥当であるかどうかも考えなければなりません。
デフォルトコンストラクタは便利な仕組みですが、無条件に用意すればよいものではなく、クラスの責務や使い方に合わせて設計することが大切です。
まとめ
C++のデフォルトコンストラクタは、引数なしでオブジェクトを生成するためのコンストラクタです。
コンストラクタを何も定義していなければコンパイラが自動で用意する場合がありますが、それだけで安全な初期状態が保証されるわけではありません。
重要なのは、次の点です。
- デフォルトコンストラクタがあることと、適切に初期化されていることは別である
- メンバの初期化はコンストラクタ本体よりメンバ初期化リストが基本である
- 引数付きコンストラクタを定義すると、自動のデフォルトコンストラクタは生成されない
= defaultで明示的に許可でき、= deleteで禁止できるconstメンバや参照メンバ、デフォルト構築できないメンバがある場合は特に注意が必要である- 初期化方法の違いによって結果が変わるため、デフォルト初期化と値初期化の違いも理解しておく必要がある
デフォルトコンストラクタを正しく理解すると、C++における「生成」と「初期化」の考え方がかなり整理しやすくなります。
クラス設計の基礎として、表面的な文法だけでなく、どのような状態でオブジェクトが作られるのかまで意識しておくことが重要です。
以上、C++のデフォルトコンストラクタについてでした。
最後までお読みいただき、ありがとうございました。
