最新情報(固定記事)

※※※※※※※※※※※※※※※※※※※※※※※
★ミナーヴァの冒険 ~従属するモノ~
発売中です!左のメニューからどうぞ!
最新:Ver 1.11d
・ミナーヴァ登録情報

★スーサイダー
予告中です!体験版準備中です!
・スーサイダー仮設ページ公開

★キャッスルえびる
開発中です!進捗25%(体験版範囲:80%)
・キャッスルえびる仮設ページ公開

------------------------------------------------------------
★更新情報(2016/12/24)
更新
-Minerva Translation Patch (English) for 1.11c or later
-Ver1.11eパッチ
・ミナーヴァ登録情報

※※※※※※※※※※※※※※※※※※※※※※※

2015年6月27日土曜日

プログラムメモ

完全に自分のメモ

■二つのライブラリをまたぐシングルトン(っぽい)クラスでエラーが出る。


class HPLSequence;
class HPLCounter : public HPLSequence;;
というカウンタクラスがあって、HPLDXLibで定義されている。

HPLDXLibに下記クラスがある。
class HPLCommonStaticData;
class AbstractDXStaticData: public HPLCommonStaticData;

HPLAir**Libに下記クラスがある。(**は綴りが長いので**で。)
class HPLAir**StaticData : public AbstractDXStaticData;
class MinervaStaticData: public HPLAir**StaticData;

そんでもって、本編(例えばミナーヴァの冒険)では下記で定義。
class GStaticData : public MinervaStaticData;
extern GStaticData staticData;

で、この状態で実行すると、MinervaStaticDataのコンストラクタ辺りで、HPLCounter/HPLSequenceがNULLになったりするなどのエラーになる。

書き方やタイミングで変わることから、再現はほぼ100%ながらも、どこに初期化を入れるかで症状が変わって来るので、もっと違う所がおかしい。

当然HPLCounter/HPLSequence自体はポインタを使ってないし、カウンタ自身もポインタでは定義されていない。

そして、MinervaStaticDataを間に噛まさずに、HPLAir**StaticDataを直接継承すると何も問題が起こらなくなる。謎。


MinervaStaticDataはアポカリでも使おうと思って共通化のために用意したが、どうにもならないので、結局MinervaStaticDataは完全に削除した。

代わりにAir**StaticDataに色々メンバを追加した形に。


以下ポイント

※メモリ破壊に近い。

再現性が不規則。予想と全く関係無いところでエラーになる。

※多重継承ではない。

多重継承は要するにclass A : public B, C;みたいに、「1つの子供に二つ以上親が居る」状態なので、今回は当てはまらない。
class A:B:C:D;みたいな感じ。
class A:C:D;までは良かったのに、class A:B:C:D;ってなった途端エラーになった感じ。




■親のデストラクタ段階では、継承した子クラスで追加される領域は既に解放されている。

こっちは理由も明確だった。

例えば下記。

class Parent {
public:
  ~Parent(){
   clear();
  }
  virtual void clear() = 0;
};

class Child : public Parent{
public:
  virtual ~Child() {}
  void clear() {}
};

Childが消える時、デストラクタ~Child()が呼ばれ、その後virtualで親のデストラクタ~Parent()が呼ばれる。
しかし、親には実体がないclear()が呼ばれている。
じゃあ、継承した子クラス=Child::clear()をとなるが、子クラスの「追加部分」(clearの実体部分含む)はもう解放されており、関数の参照先が存在しない形になる。

そのため、これを実行するとNULLになったり参照エラーになる。

対処方法は幾つかある
[1]使わない
Parent p;
p.clear();
などとして、デストラクタは~Parent(){}とかにする。
×欠点として、外部から明示的に呼ぶ必要が出てくる。

[2]子の方で呼ぶ
Child::~Child(){ clear();}
Parent::~Parent(){}
とかにする。
×まあ、上手くはいくが、何のためのabstract funcなのか分からんという、デザインパターン的な意味が無くなる問題がある(気にしなきゃいいけど)
×どっちで呼べばいいのか分からなくなると言う問題もある。

[3]引数で回避する
Parent::release(bool bisClear){
 if(bIsClear){
  this->clear();
 }
}
Parent::~Parent() { release(false);}
こうする。
要するに、他でも呼ばれるclearを使うのがまずいので、それを回避する形。
Child側で解放したいモノがあれば、~Child(){}内で解放すれば良いわけであるし。


いずれにしても分かりづらい。なるべく親は親、子は子で解放は分けた方が良い。


■他


※virtualは、子の関数「が終わった後に」親の関数を呼ぶ。

逆ではない。
「先に親の関数を呼びたい」場合は下記にする。
class Parent {
  void hoge() {
  }
};

class Child {
 void hoge(){
    Parent::hoge();
    処理
  }
}
・Parent::hoge()が実行されたのち、処理が実行される。

・virtualが無いので、親の関数=Parent::hoge()は「明示的に指定しない限り呼ばれない」完全にオーバーライド(override)された状態となる。

上記でvirtualを付けると、Parent::hoge()→処理→Parent::hoge()ということか?(未検証)

※virtual func(){}とvirtual func()=0;

・virtual func(){}は、親の関数もこのあと呼ぶみたいな感じ。親、子、いずれも実体定義必須。
・virtual func()=0;は実体を持たない。継承した子クラスが定義する必要有り。


0 件のコメント:

コメントを投稿