コンストラクタは、オブジェクトを作成し、メンバ変数を初期化するための特別なメンバ関数の一種であり、C++ では、コンストラクタはクラスと同じで、戻り値はありません。
以下の場合には、カスタムコンストラクタが必要です:
- クラスのメンバの初期化方法をカスタマイズする場合
- クラスのオブジェクトを作成する際に関数を呼び出す場合
コンストラクタは通常
public
ですが、protected
、private
などに宣言することもできます。
デフォルトコンストラクタ#
デフォルトコンストラクタは、引数を提供しないコンストラクタであり、クラスの定義時に明示的に定義する必要はありません。明示的に定義しない場合、コンパイラはデフォルトコンストラクタを自動的に生成します。デフォルトコンストラクタの例は次のとおりです:
class MyClass{
public:
MyClass(){
// 何かを行う..
}
}
デフォルトコンストラクタの「最も困難な解析」について#
C++ の解析プログラムは、宣言に偏っているため、エディタが生成したデフォルトコンストラクタを呼び出して括弧を使用しようとすると、警告が発生します:
class MyClass{};
int main(){
MyClass myClass();
// 警告 C4930
}
この例では、MyClass myClass (); は、MyClass のデフォルトコンストラクタを呼び出して myClass オブジェクトを作成することを期待していません。代わりに、この行のコードは実際には関数宣言として解釈され、オブジェクト宣言ではありません。ここでは、myClass は引数を取らず、MyClass 型のオブジェクトを返す関数として解釈されます。
この現象は、「最も困難な解析」(Most Vexing Parse)と呼ばれ、C++ 言語の有名な構文解析の問題です。この問題は、C++ の構文の複雑さに起因し、一部の構文構造が文脈に応じて複数の異なるものとして解釈される可能性があるため、コンパイラは式を関数宣言ではなくオブジェクトの構築として解釈することを優先します。
この問題を回避するために、オブジェクトを明示的に作成します:
int main() {
MyClass myClass;
}
パラメータ化コンストラクタ#
パラメータ化コンストラクタは、複数の引数を持つ初期化を行います:
class MyClass{
public:
int x, y;
MyClass(int a, int b): x(a), y(b){
// 何かを行う..
}
}
ここでは、メンバイニシャライザリストを使用しています。式の具体的な形式は次のとおりです:
identifier(argument)
メンバイニシャライザリストは、コロンの後に続くすべての式で構成されます:
MyFunc(int a, int b, int c):
m_a(a), m_b(b), m_c(c){}
コピーコンストラクタ#
コピーコンストラクタは、同じ型のオブジェクトを使用して別の新しいオブジェクトを初期化するために使用されます。通常、オブジェクトのコピーのシナリオで使用されます:
class MyClass{
public:
int x, y;
MyClass(const MyClass& mc):
x(mc.x), y(mc.y) {}
}
上記の単純な例では、メンバはスカラーですが、このような単純な場合は、コンパイラがコピーコンストラクタを生成するため、自分で定義する必要はありません。ただし、コピーコンストラクタに特殊な動作が必要な場合、たとえば新しいメモリを割り当てる場合は、自分でコピーコンストラクタを定義する必要があります。
コンストラクタを定義する際には、コピー代入演算子=
をオーバーロードする必要があります。
ムーブコンストラクタ#
ムーブコンストラクタは、C++11 以降の新機能であり、リソースを 1 つのオブジェクトから別のオブジェクトに移動するために使用されます。通常、右辺値参照をサポートするシナリオで使用され、既存のオブジェクトのデータの所有権を新しい変数に移動します。元のデータをコピーしないため、大きなオブジェクトを渡す場合にプログラムの効率を大幅に向上させることができます。 それは、rvalue 参照を最初のパラメータとして取り、後続のパラメータはデフォルト値を持たなければなりません。 ムーブコンストラクタは、大きなオブジェクトを渡す場合にプログラムの効率を大幅に向上させることができます:
//todo 左辺値と右辺値
class MyClass{
public:
char *str;
MyClass(MyClass && obj): str(obj.str){
obj.str = nullptr; // 元のオブジェクトが破棄されるときにメモリが解放されるのを防ぐ
}
}
デリゲートコンストラクタ#
デリゲートコンストラクタは、C++11 以降の新機能であり、別のコンストラクタを現在のクラス内で呼び出すことを許可します。これにより、コードの解耦がさらに進みます:
class MyClass{
public:
int x, y, z;
MyClass(int a, int b):x(a), y(b), z(0) {}
MyClass(int a, int b, int c): Point(a, b) {z = c;}
}
継承コンストラクタ#
継承コンストラクタは、C++11 以降の概念であり、基本クラスからコンストラクタを継承し、using
キーワードを使用して実現します:
class Base{
public:
Base(int x){};
};
class MyClass: public Base{
using Base::Base; // Baseのコンストラクタを継承する
}
明示的コンストラクタと暗黙的コンストラクタ#
explicit
キーワードで宣言されたコンストラクタは、直接初期化にのみ使用でき、暗黙的な変換時に誤って呼び出されることを防ぎます:
class MyClass {
public:
explicit MyClass(int a) {}
};