C++ でクラスコンストラクタを実装する
この記事では、C++ でクラスコンストラクターを実装する方法を紹介します。
コンストラクターとは何か、C++ でどのように機能するか
コンストラクターは、クラスオブジェクトの初期化方法を定義する特別なメンバー関数です。コンストラクターは通常、クラスのデータメンバーを初期化し、クラスオブジェクトの作成時に実行されます。コンストラクター関数のいくつかの特定の機能は、それらがクラス自体と同じ名前を持ち、戻り型を持つことができないことです。通常、クラスには相互にオーバーロードする複数のコンストラクターがありますが、パラメーターの数またはタイプが異なる必要があります。
コンストラクターは、少し複雑なクラスでユーザーによって明示的に指定されることが多いことに注意してください。それでも、ユーザーがコンストラクターを定義しない場合、デフォルトのコンストラクターはコンパイラーによって自動的に生成されます。デフォルトのコンストラクターは通常、引数をとらないコンストラクターであり、クラスのデフォルトの初期化のために呼び出されます。ただし、コンパイラによって生成されたデフォルトコンストラクタは、正式には合成デフォルトコンストラクタと呼ばれることに注意してください。後者は、データメンバーに従ってクラスごとに具体的に推測され、クラス内初期化子またはデフォルト値を使用してメンバーを初期化します。したがって、自動生成されたコンストラクターは普遍的なソリューションではありませんが、単純なクラス構造では正しく機能します。
次の例では、2つのコンストラクターを使用して MyClass1
クラスを定義しました。最初のものは引数を取らないことに注意してください。これは、それがデフォルトのコンストラクターであることを意味しますが、それでも default
キーワードを指定しました。後者は、この特定のコンストラクターをデフォルトのコンストラクターにする必要があることをコンパイラーに示します。一般に、ユーザーがコンストラクターを定義した場合、コンパイラーはデフォルトのコンストラクターを生成しません。この場合、ユーザーは、指定されたコンストラクター関数の default
指定を明示的に要求する必要があります。
MyClass1
の 2 番目のコンストラクターは、引数として単一の string
値を取り、それを使用して name
データメンバーを初期化します。特別な文字列リテラルを cout
ストリームに出力して、関数の実行瞬間を観察できるようにします。m2
オブジェクトの作成により、コンストラクターがトリガーされます。m1
オブジェクトはデフォルトのコンストラクターで初期化され、コンパイラー自体が後者のコンストラクターを生成するため、cout
ストリームに出力された文字列は表示されません。
#include <iostream>
#include <utility>
using std::cout;
using std::endl;
using std::string;
class MyClass1 {
private:
string name;
string nickname;
public:
MyClass1() = default;
explicit MyClass1(string n) : name(std::move(n)) {
cout << "Constructor 1 is called" << endl;
};
string getName() { return name; }
string getNickname() { return nickname; }
~MyClass1() { cout << "Destructor is called" << endl; }
};
int main() {
MyClass1 m1{};
cout << m1.getName() << endl;
cout << m1.getNickname() << endl;
cout << "------------------" << endl;
string n1("Maude");
MyClass1 m2(n1);
cout << m2.getName() << endl;
cout << m2.getNickname() << endl;
cout << "------------------" << endl;
return EXIT_SUCCESS;
}
出力:
------------------
Constructor 1 is called
Maude
------------------
Destructor is called
Destructor is called
C++ でオーバーロードを使用して複数のクラスコンストラクターを実装する
MyClass1
には、nickname
と呼ばれる 2 番目の string
データメンバーがあります。単一の string
値を取り、それを定義して nickname
を初期化する別のコンストラクターを作成するとします。その場合、コンパイラーは、同じパラメーターで関数をオーバーロードできないというエラーを発生させます。したがって、別のコンストラクターを定義する必要があります。たとえば、2つの string
参照を受け取り、両方のデータメンバーを初期化するコンストラクターを選択しました。次のコードスニペットを実行すると、2 番目のコンストラクターが実行されたことがわかります。
コンストラクターにははるかに詳細な特性があり、この記事では主な機能のみを紹介しました。他の特別な関数には、move-constructor や copy-constructor のように、名前に-constructor
という単語が含まれています。これら 2つは、まとめてコピー制御
と呼ばれる特殊操作の一部です。デストラクタは、コンストラクタとは逆の動作をすることに注意してください。つまり、クラスメンバーの割り当てを解除し、通常、オブジェクトがスコープ外になると自動的に呼び出されます。与えられたコードスニペットを使用して、コンストラクタ-デストラクタ呼び出しの動作を簡単に観察できます。
#include <iostream>
#include <utility>
#include <vector>
using std::cout;
using std::endl;
using std::string;
class MyClass1 {
private:
string name;
string nickname;
public:
MyClass1() = default;
explicit MyClass1(string n) : name(std::move(n)) {
cout << "Constructor 1 is called" << endl;
};
// ERROR: Does not Compile
// MyClass1(string nk) : nickname(nk) {
// cout << "Constructor 3 is called" << endl;
// };
MyClass1(string &n, string &nk) : name(n), nickname(nk) {
cout << "Constructor 2 is called" << endl;
};
string getName() { return name; }
string getNickname() { return nickname; }
~MyClass1() { cout << "Destructor is called" << endl; }
};
int main() {
string n1("Maude");
string n2("Bibi");
MyClass1 m4(n1, n2);
cout << m4.getName() << endl;
cout << m4.getNickname() << endl;
cout << "------------------" << endl;
return EXIT_SUCCESS;
}
出力:
Constructor 2 is called
Maude
Bibi
------------------
Destructor is called
Destructor is called