書籍転載
文法からはじめるプログラミング言語Microsoft Visual C++入門
C++のクラスをマスターしよう(前編)
―第10章 クラス〜オブジェクト指向プログラミング(前編)―
WINGSプロジェクト 矢吹 太朗(監修 山田 祥寛)
2010/05/19 |
 |
|
●10.1.6 コンストラクタ
オブジェクトを生成する際には、コンストラクタという特殊なメソッドが呼び出されます。コンストラクタは次のように定義します(宣言と定義を分けることもできます)。普通のメソッドと違って、コンストラクタには戻り値がないことに注意してください。
引数のないコンストラクタをデフォルトコンストラクタ(既定のコンストラクタ)と呼びます。プログラマがコンストラクタを定義しない場合には、空のデフォルトコンストラクタがコンパイラによって生成されます。つまり、10.1.2項のクラスPersonの定義は、次のコードと同じです。
struct Person
{
string name;
int age;
//デフォルトコンストラクタ
Person() {} //この記述がないと、コンパイラが自動生成する。
}; |
デフォルトコンストラクタを次のように実装すれば、オブジェクトが生成されるときにメッセージが表示されます。
#include <iostream>
using namespace std;
struct Person
{
string name;
int age;
Person() { cout<<"オブジェクトが生成された\n"; }
};
int main()
{
Person x;
//出力値:オブジェクトが生成された
} |
|
[サンプル]10-constructor1.cpp |
コンパイラによって自動生成されるのは、デフォルトコンストラクタだけではありません。次のようにクラスを定義したとしましょう。
struct Person
{
string name;
int age;
};
|
|
[サンプル]10-defaultmethods.cpp |
このコードをコンパイルすると、デフォルトコンストラクタの他に、次のようなメソッドが自動生成されます*4。
- コピーコンストラクタ:別のオブジェクトをコピーして新しいオブジェクトを生成する
- 代入演算子:オブジェクトを別のオブジェクトで上書きする
- デストラクタ:オブジェクトを削除する際に呼び出される(既定では機能はない)
*4 C++0x では、これらのメソッドの自動生成を禁止することができるようになる予定です。 |
次のような自明に見える操作ができるのは、自動生成されたこれらのメソッドのおかげです。
Person taro; //デフォルトコンストラクタの利用
taro.name="Taro";
taro.age=32;
Person a(taro); //コピーコンストラクタの利用
//Person a=taro; //でもよい
cout<<a.name<<endl;//出力値: Taro
Person b;
b=a; //代入演算子の利用
cout<<b.name<<endl;//出力値: Taro |
|
[サンプル]10-defaultmethods.cpp |
このPersonのような簡単なクラスの場合には、自動生成されるメソッドで十分なのですが、クラスによっては自動生成されるメソッドでは機能が不十分だったり、動作が期待どおりではないことがあります。そのような場合には、これらのメソッドを自分で定義し直します。
たとえば、オブジェクトの生成と同時に名前と年齢を初期化するようなコンストラクタが必要な場合には、次のように自分で定義しなければなりません。
#include <iostream>
#include <string>
using namespace std;
struct Person
{
string name;
int age;
//名前と年齢を引数に持つコンストラクタ
Person(string name, int age)
{
this->name=name;
this->age=age;
}
//デフォルトコンストラクタを自分で実装する
Person() {}
};
int main()
{
Person taro("Taro", 32); //コンストラクタの利用
cout<<taro.name<<endl; //出力値: Taro
Person p; //デフォルトコンストラクタを利用
Person people[5]; //デフォルトコンストラクタを利用
} |
|
[サンプル]10-constructor2.cpp |
このコードでは、名前と年齢を引数に持つコンストラクタを実装しています。このコンストラクタでは、this->name=name;などとして、フィールドにパラメータを代入しています。thisは自分自身(Personオブジェクト)を指すポインタです。フィールドとパラメータの名前が違う場合には、this->という記述は不要ですが、この例ではどちらもnameなので、区別できるようにフィールドは単なるnameではなくthis->nameとしています。フィールド名を_nameのようにしておいてもよいでしょう。もちろん、コンストラクタの宣言や定義をPerson(string s, int a)のようにすれば名前は重複しませんが、パラメータの意味がわかりにくくなってしまいます。
独自のコンストラクタを定義すると、コンパイラはデフォルトコンストラクタを自動生成しなくなることに注意してください。ですから、引数なしでオブジェクトを生成したい場合や、オブジェクトの配列を生成したい場合には、デフォルトコンストラクタを自分で実装しなければなりません。
実は、この例のnameやageの設定方法には改善の余地があります。このリストのコンストラクタで行われているのは、初期化ではなく代入です。つまり、一度空のフィールドnameが生成されてから、パラメータの値が代入されるので、効率が悪くなるかもしれないのです(フィールドageは組み込みデータ型なので、初期化と代入に違いはありません)。
フィールドを初期化するためには、コンストラクタの定義において、次のようなコンストラクタ初期化子を使います。
クラス名(パラメータリスト) : フィールド名(値),... |
|
[構文]コンストラクタ初期化子 |
先の例のコンストラクタは、次のように書くべきでした。これでnameとageが初期化されます。
Person::Person(string name, int age) : name(name), age(age) {} |
初期化と代入の違いは、後で紹介するコピーコンストラクタと代入演算子を比較するとよくわかります。
Insider.NET 記事ランキング
本日
月間