コンストラクタはオブジェクトの生成されるときに必ず呼ばれる関数。
オブジェクトの初期化をするために使う。
必ずコンストラクタが実行されるため
オブジェクトは一貫した形式で初期化できる。
class Test {
};
// インスタンス t が生成されるときに t のコンストラクタが自動で実行される。
Test t;
■ コンストラクトする順番
POINT
建物と同じように考えるとわかりやすい。
基礎ができて -> 飾り が追加される
破棄の際は逆
■ 初期化子リスト
クラスのメンバオブジェクトを初期化するには初期化子リストを使う。
コンストラクタ本体の実行の前に、しておくと効率がよい。
class Test {
string m_str;
// メンバオブジェクト m_str を初期化する。
Test::Test() : m_str( "" )
{
// コンストラクタ本体では空ということも多い。
}
WARNING
Test::Test()
{
// 代入では効率がよくない。
m_str = "";
}
};
コンストラクタはすべてのメンバと、基底クラスを初期化する。
初期化子リストを明示しない場合でも、コンパイラが自動で初期化する。
■ コンストメンバの初期化
const メンバは初期化子リストのタイミングのみ初期化できる。
class Test {
const string m_str;
// メンバオブジェクト m_str を初期化する。
Test::Test() : m_str( "init" )
{
// 初期化の代入は const に反するのでエラーとなる。
m_str = "test";
}
リファレンスのメンバも初期化リストで必ず初期化する。
class Test {
const string &m_str;
// メンバオブジェクト m_str を初期化する。
Test::Test( const string &s ) : m_str( s )
{
// 初期化の代入は const に反するのでエラーとなる。
m_str = s;
}
■ 初期化子リストの順番
クラスのメンバ変数は、クラスの宣言順に初期化が実行される。
基底のクラスの初期化 ---> メンバオブジェクトをクラスの宣言順に初期化
class Test : public Base{
int i0;
int i1;
int i2;
};
// 並べた順ではなく 0, 1, 2 の順番で初期化される。
Test::Test() : i2(2), i1(1), i(0) {}
実際に初期化される順番でかくと間違いをふせげる
Test::Test() : Base(), i0(0), i1(1), i2(2) {}
WARNING
メンバの初期化で別のメンバの値を利用するときは、順番に注意すること。
クラスのメンバの順番を変えただけで、エラーをおこす。
// コンストラクタ本体で代入すれば、順番の依存性を避けることができる。
■ DefaultConstructor(デフォルトコンストラクタ)
引数をもたないコンストラクタをデフォルトコンストラクタという。
クラスの constructor は object 生成時に必ずコールされる。
コードで定義しない場合は, コンパイラが自動生成する。
class T {
int a;
public:
T(){}
};
main(){
// ローカルのオブジェクト a が生成され、DefaultConstructor( 引数なしのコンストラクタ )コールされる
T t;
}
■ 配列のコンストラクタ
POINT
配列の場合は、各要素はデフォルトコンストラクタが起動する。
Test a[10];
Test *t = new Test[10];
そのためデフォルトコンストラクタがないクラスはコンパイル時にエラーとなる。
class Test {
public:
Test( int i );
};
初期化したいときは init() などのメソッドをつかって初期化をする。
Test *t = new Test[10];
for( int i=0; i< 10; i++ ){
t[i].init();
}
または std::vector を利用すると各要素のコンストラクタを指定できるので便利。
vector< Test > a( 10 );
vector< Test > a( 10, Test(2) );
REFERENCE vector
constructor は object [ 生成時 ]に[ 必ず ]コールされる
返り値をかえせない. ( ObjectAdr を返す必要があるから )
-> Object 自身の型を返す
Foo &Foo();
-> ので次のような記述は可能.
Foo func()
{
// Retval を返す際に, CopyConstructor が走る. ie. Foo( const Foo &);
return Foo();
}
POINT
ctor も関数のひとつ. ( Access 制限をうける )
private() にすると自分以外がつくれなくなる
bCls::ctor を call するのは dCls::ctor
-> ので, protected: bCls::ctor && dCls::ctor ならば次は可能.
b *b = new dCls; // ただし B 単体でつくれない.
Class の Component は INIT 時に DefaultCostructor が call される.
-> Cmp::Cmp( int ){} ど定義してしまえば, 明示的に呼ぶ必要あり.
class App {
public:
App(){
// 暗黙に c が生成され, Cmp::Cmp() が call
#if EXPLICIT
c(); // CMP が自動生成. ( なければ CMP ERROR )
#endif
}
private:
Cmp c; // Pointer ではなく, object 自身をもつのもあり. ( heap に作成される ? )
// 内包している分サイズが増大する.
};
class foo {
int a;
public:
foo(){ ... }
}
■ CopyConstructor(コピーコンストラクタ)
DESC
引数に自分と同一クラスをもつ コンストラクタのこと。
値渡し ( PassByValue ) で呼ばれる。
同じ型の 別の Object で "初期化" するときに使う
Foo func() {
Foo *f = new Foo();
return *f; // ここでコールされる
}
POINT
代入演算子と間違えないこと。
インスタンスが生成されるタイミングでコピーコンストラクタは呼ばれる。
Foo a, b;
// CopyConstructor
{
Foo f( a );
Foo f = a;
}
// これはコピー代入演算子
f = b
// 値渡し でもコールされる。パラメータ側のオブジェクトが生成されるから。
// Foo func( Foo );
Foo d = func( f );
■ コピーコンストラクタの自動生成
コンパイラが自動生成する場合は すべてのメンバのコピーコンストラクタを実行する
組みこみ型は bit 単位のコピーがされる。
■ コンストラクタ内で別のコンストラクタを呼ぶ
コンストラクタ内で別のコンストラクタをよぶと別オブジェクトが生成される。
コードを共有したい場合は init() などのメソッドを用意する。
class Test {
public:
Test();
Test();
private:
void init();
};
■ コピーを禁止する
クラスのコピー操作を禁止するには private のアクセスにすることでコピーを利用できないようにする。
File クラスなどはコピーが不要なので、禁止してしまう。
class File {
File( string &path );
private:
// 利用しないので定義はしない
void File( const File & );
}
File f;
// ここでコンパイラがエラーを教えてくれる。
File b = f;
■ ShallowCopy.DeepCopy
ShallowCopy
ポインタだけをコピーすること
DeepCopy
ポインタの指す参照先も含めてコピーすること。
■ コンストラクト方法を制御する
コンストラクタを private 権限にすることで
自分以外がオブジェクトを生成できなくすることができる。
この場合は create() をするスタティックなメソッドを代わりに用意してあげる。
ファイルクラスはロードするクラスのみが管理するようにする。
REFERENCE friend
class File {
// ローダにだけ自分の private を公開する
friend Loader;
private:
// 封印する
File( const string &path );
~File();
};
class Loader() {
public:
File *createFile( const string &path ) {
return new File( path );
}
void destoryFile( File * );
};
{
// Loader の管理しない File を作成することができない
File f;
}