winAppDev > 重複includeの連鎖を断ち切る / 派生クラス > 前方宣言の利用


http://qiita.com/7of9/items/ff461543465a621d4539
の問題の再検討。

動作確認
C++ Builder XE4

前提

使用したいフォーム(ConnectForm)については、Form->Show();処理しかしていない。
それ以外の処理をする場合は、スライス問題なども気にする必要があるかも。

includeは以下のような感じ

  • grp3141Main.h > param3141.h > enum COLOR_e { ... }
  • grp9999Main.h > param9999.h > enum COLOR_e { ... }

param3141.hのインクルードガード : #ifndef PARAM3141_H
param9999.hのインクルードガード : #ifndef PARAM9999_H

多重includeの問題

ConnectForm.cpp
#include "grp3141Main.h"
#include "grp9999Main.h"
...
TFormBaseDelegate * __fastcall TFormConnect::selectForm(int idx)
{
    switch(idx) {
    case 3141:
        return Form3141Main;
    case 9999:
        return Form9999Main;
    }

    return NULL; // not found
}

上記があることで、
- grp3141Main.h内のincludeに含まれるparam3141.hがincludeされる。
- grp9999Main.h内のincludeに含まれるparam9999.hもincludeされる。

結果として、両方のparamヘッダーで定義されているenum COLOR_e { ... }が重複定義となる。

前方宣言の利用

includeをやめて以下のようにする。

ConnectForm.cpp
// #include "grp9999Main.h"
class TForm9999Main;
extern TForm9999Main *Form9999Main;
...

この変更により、enum COLOR_eの重複定義はなくなったが、return Form9999Main;の行で以下のエラーが出る (Form9999Mainの宣言にて派生元を定義していないため)。

E2034 'TForm9999Main *'型は'TFormBaseDelegate *型'に変換できない。

以下のように対応した。

ConnectForm.cpp(続き)
TFormBaseDelegate * __fastcall TFormConnect::selectForm(int idx)
{
    switch(idx) {
    case 3141:
        return Form3141Main;
    case 9999:
        return (TFormBaseDelegate *)Form9999Main;
    }

    return NULL; // not found
}

一応対応はできたが、上記のキャストはC++のものでない。

C++のキャストとして
return static_cast<TFormBaseDelegate *>(Form9999Main);
とすると以下のエラーとなった。

E2031 'TForm9999Main *'から'TFormBaseDelegate *'へのキャストはできない

以下ではできたが、これが正しいキャストなのかは未消化。
return reinterpret_cast<TFormBaseDelegate *>(Form9999Main);

備考

今回の対応は、単純に複数のフォームを切り替えて開く場合に、include経路により多重定義が発生する問題の対応を行った。
include先ファイル自体を編集していくより、前方宣言を利用する方が全体的なコードの変更量を抑えることができると思う。

それぞれのフォームのメンバ関数など色々使う場合に対応可能かは未検証。