初期化リストにおける基底クラスの初期化順序

基底クラスの初期化につづけてメンバー変数の順で初期化されるから注意しましょうという話。

テストケースをつくるときに、にかよった設定をするオブジェクトがたくさんあるので、そのベースクラスをつくり、ちょっとした違いを派生クラスでカバーしようと考えた。

つまり以下のようなコードで、 base が設定対象、あたえる設定が memb、そして base に基本設定をあたえる Base、あとは Base をちょこちょこ派生して差分プログラミング、とおもったわけ。

class base { ... };
class memb { ... };
class Base : public base {
protected:
 const memb ver;
 const memb key1;
 const memb val1;
 ...
public:
 // 初期化順序問題が発生する
 Base() : ver(...), key1(...), val1(...), ... , base(ver) {
  put(key1, val1); // put() is base's member function
  put(key2, val2);
  ...
 }
 ...
};

ところが残念なことにこのコードは動かない。
問題は Base の初期化リスト末尾にある base に渡した変数 ver。
冒頭にかいたとおり、ベースクラス、その後にメンバー変数の順で初期化されるため、 Base の初期化リストの末尾に書いた base コンストラクターに渡す ver が、初期化される前に base に渡されてしまう。
Base に用意した設定用変数群を static に取るという荒技は、コンストラクターやデストラクターを伴う非 POD なオブジェクトに対しては使いたくない。

この場合は ver をメンバーから外して、初期化リストで base(memb(...)) と書いてやれば解決する。
そして基底クラスは初期化リストの一番はじめにならべておくのがいいだろう。

それにしても、初期化順序問題、こんなところにも隠れているから怖い。