今日のメニュー:特殊食材(その2):Constructor & Destructor

今日のポイント

昨日の実習の copy constructor と operator=、くどいけど、もっかいおさらい。

 

特殊食材:C++ で継承されず自動生成される member function たち

明示的に実装されていない場合、

  1. default constructor
  2. copy constructor
  3. default destructor
  4. default operator=

は、親クラスのものが継承されず、自動生成される。
これらの member function が継承されないのは子クラスに新たな data member が加えられた場合、そのメモリー領域の確保あるいは解放、複製のため、親クラスのものを継承することができない(無理にするとまともに動かないことは容易に想像できるであろう)ためである。ただし、継承されないと言うことは、必ずしも親クラスのものが使われないと言うことを意味しない。実際、これらの member function が自動生成された場合には、以下に述べるように、親クラスへと連鎖する。

 

Constructor の動作

constructor の作業は

  1. initializer で親クラスの constructor が指定してあれば、親クラスの constructor を指定された引数で呼ぶ。指定してなければ、親クラスの default constructor を呼ぶ。
  2. initializer で data member の constructor が指定してあれば、それらをリストの順に指定された引数で呼ぶ。指定してなければリストを補完し data member の default constructor を呼ぶ。
  3. constructor 本体(にょろ括弧の中身)を実行する。

の順で行われる。

注意すべきなのは、1〜2にあるように自分で constructor を実装した場合、仮に親クラスやいくつかの data member の constructor を明示的に呼んでいなくても、それらの default constructor を呼ぶように initializer が補完されるということである。つまり、こうすることにより、先祖代々にわたって全ての data member のメモリー領域の確保が、少なくとも default constructor が呼ばれることによって保証される。

これは copy constructor でも同様である。copy constructor を明示的に実装した場合、親クラスの copy constructor への連鎖を initializer や constructor 本体で明示的にしない場合でも、default constructor は連鎖していく。ただし、この場合、親クラスの data member のコピーが行われないことは言うまでもない。また、initializer ではなくて copy constructor の本体で単純代入により data member のコピーを行う場合があるが、これは、initalizer にその data member の default constructor が補完されることでにょろ括弧に入る前に作られた data member の instance に対する代入演算を行っていることになる。単純代入で良いのならば intializer で copy constructor を使うべきである。

int、float、double などの単純型の initializer も copy constructor と見なせることに注意する。この場合も、にょろ括弧の中で代入する場合には、initializer にこれらの単純型の data member の default constructor が補完され、そこで単純型オブジェクトが default construct されてから、本体で代入が起こると考えられる。


自動生成の default constructor の場合、上の1〜3に対応させると、

  1. 親クラスの default constructor を呼ぶ。
  2. data member の default constructor を順に呼ぶ。
  3. 本体は空。

となる。1〜2は自動生成で initializer が生成されたと解釈できる。

自動生成の copy constructor の場合は、

  1. 親クラスの copy constructor を呼ぶ。
  2. data member の copy constructor を順に呼ぶ。
  3. 本体は空。

となる。この場合も1〜2は自動生成で initializer が生成されたと解釈できる。

 

● 注意

  1. initializer list は、data member の定義された順に並んでいなくてはいけない。その順でメモリー領域が確保されるからである。この約束を守らずにコンパイラーに怒られた経験があるのは私だけではあるまい。
  2. class の instance を初期値付きで作成する場合、つまり
    TaClass a = b;
    の場合は、a が default construct されてからそれに b を代入する演算が行われるという解釈もあり得るが、実際には、これは
    TaClass a(b);
    と同値であり、TaClass の copy constructor が呼ばれるのであって、operator= は呼ばれない。

 

Destructor の動作

destructor の作業は、constructor の場合を正確に逆にたどることになる。
つまり、

  1. destructor 本体(にょろ括弧の中身)を実行する。
  2. data member の destructor を最後から逆に呼ぶ。
  3. 親クラスの destructor を呼ぶ。

の順で行われる。2〜3は暗黙であり、自動生成の destructor の場合は1がないのでこの部分だけとなる。2〜3により、destructor は常に連鎖する。

constructor は多重的議できるが、destructor は各クラスに1つしかないので、constructor の場合のように連鎖が途切れることはない。