環境:VisualStudio2005 MFC
派生クラス内のpublicなローカルクラスを
基底クラスのメソッド引数にすることは可能でしょうか?
可能だとすると,
その基底クラスのメソッドの宣言はどのように書けばよいのでしょうか?
なんらかの前方宣言のようなものが必要と思うのですが…
以下のようなコードではコンパイルエラーになってしまいます.
エラーメッセージ:
error C2027 認識できない型 'CDeriv' が使われています
error C2061 構文エラー:識別子 'LocalClass'
-----------------------------------------
class CDeriv; //CDerivを前方宣言
//class CDeriv::LocalClass; //←こう書くこともできないようだ
class CBase
{
void Method( class Deriv::LocalClass * ); //←これではコンパイルエラー
};
class CDeriv : public CBase
{
public:
class LocalClass{ ... };
};
class CDeriv;
class CBase
{
void Method( class Deriv::LocalClass * ); ←この時点で CDeriv は定義されてな
いとだめ
};
class CDeriv : public CBase ←この時点で CBase は定義されてないとだめ
{
public:
class LocalClass{ ... };
};
ということで変なグルグルができちゃってるので
しいて言うなら
class CBase
{
void Method(void* localClass);
};
class CDeriv : public CBase
{
public:
class LocalClass{ ... };
};
void CBase::Method(void* localClass)
{
(CDeriv::LocalClass*)localClass; ←void* をこの時点で 化けてみる?
}
ただ。。。
や・や・こ・し。。。
それはつまり基底クラス base が、「ある特定の派生クラス derived1 に依存する」と
いうこと。別の派生クラス derived2 から base::method() を呼べないということ。
やはり設計がおかしいとしか言いようが無いなぁ。
その derived1::localclass は derived2 からも使える必然があるのかないのか?
必然があるのであれば base::localclass であるべき、なのだろう。
# あるいは完全に独立した ::localclass であってしかるべき。
およそ localclass という名前の作業クラスを持っているクラスならば
method() を呼べるようにしたい、というのであれば generics (template) 。
base が derived に依存するという時点であまり適切な設計ではないと思われる。
俺ならクラス依存関係を見直しすることをお勧めする。
ローカルクラスは、関数スコープ内などで宣言されるクラス、とする人が
多いようです。本件のものは内部(inner)クラスだと思います。
これらの宣言を使いたい理由のほとんどは「局所的に宣言し、使用したい」
というもので、むしろ外部に公開したくない、というのが動機のほとんど
であると考えられます。
従って、内部クラスを外部で使用したいということ自体が矛盾しているといえます。
どうしても似たようなことがしたいのなら、公開されているクラスを継承した
内部クラスを作成し、使用することになるでしょう。インターフェースは
公開されているクラスになります。
最も「そんなの設計がおかしい」に決まってますけど(笑)。
class A{};
class Base{
public: void Func( A *a){}
};
class Drv : public Base{
class AA : public A{};
void DrvFunc()
{
AA aa;
Func( &aa);
}
}
何をしようとと思いましたか?
色々想定してフォローしてみようと思ったけど,まったく一つも思い浮かばなかっ
た。。。
皆様 ご回答いただきありがとうございます
実際に質問のような設計をせねばならない状況に陥っているわけではなく
単純に「可能か? 可能ならばどう書くのか?」
といったことが知りたかったのですが
そもそも設計がおかしい→やるべきではない
ということでまずは納得しました.
最初の質問では継承関係が絡んだものとなっていましたが,
ちなみに継承の有無に関係なく
内部クラスや内部構造体を前方宣言したいとしたらどう記述するのでしょうか?
(質問文のような継承関係でなければ
宣言順で解決できるので そもそも前方宣言する機会がないような気はしますが)
>内部クラスを外部で使用したいということ自体が矛盾しているといえます
例えば,↓のようなコード
class CClass
{
public:
//メソッドSetting()に渡すためのデータ構造
struct SettingInfoStruct
{
int a,b,c,...; //たくさんの情報
};
public:
void Setting( const SettingInfoStruct & );
{
//構造体メンバ値を使って自身を設定する
}
};
のSettingInfoStructのような,CClassの設定のためだけに使うような構造体等は
CClass内部で宣言したいように感じてしまうのですが…
うーん、クラスにデータを設定するためだけの話なら
内部クラスは作成しないような気がします。
データを取り纏めて取り扱うと言う目的はそもそもそのクラス自身で
事足りるはずです。まとめて移動したりするのであれば、クラスの
インスタンスを使えば良い話。
まとめて設定する関数の場合、引数が増えてしまうのは致し方ないと
思います。どちらにしてもその構造体に設定をする必要があるわけで
かえって手間が増えるだけのような気がします。
他の暮らすとのインターフェイスに使うなら意味がありそうですが、
それなら内部クラスにはしませんよね。
逆にあまりにもたくさんのメンバー変数がある場合であれば、
実際にはそれらのメンバー変数は何らかの意味付けで分類できるのでは
無いかと思います。もしそうであれば、そのグループ毎に意味のある
クラスとして内部クラスではなく、独立したクラスとして宣言できそうです。
内部クラスはそのクラス内部でしか使わない外部で単独で使っても
意味を持たないようなものだと認識しているので、そもそも外部から
扱われるような物ではない様に感じます。
内部クラスの前方宣言の解決方法はわかりませんが、
そのクラスでしか使われないなら内部クラスを外部に公開することは
問題ないのではないでしょうか。
(MFCはどうかわかりませんが、.NET Frameworkには外部に公開されている
内部クラスというのはいっぱいありますし)
ちなみにC#では前方宣言とかなしに普通にできることなので
C++/CLIでもできるのでは?・・・と思ったらやっぱり書き方がわかりませんでしたorz
>CClass内部で宣言したいように感じてしまうのですが…
やりたいと思うのならそうすればよいですね。実際のところ
提示のコードは(;)のミス以外には何の問題もありません。
ただし、そもそもstruct は、「全てがpublicなクラス」
と同等であることを忘れてはなりません。
つまり、上を加味してご要望を言い換えると、
「CClassクラスのメンバを初期化するのに、あえて、内部クラスである
SettingInfoStructクラスを定義しなおして、かつそれを引数とする
メンバー関数によって行う」
ことになり、自分には笑っちゃうくらいばかげて聞こえます。
どうでしょう。
> 最初の質問では継承関係が絡んだものとなっていましたが,
> ちなみに継承の有無に関係なく
> 内部クラスや内部構造体を前方宣言したいとしたらどう記述するのでしょうか?
他の掲示板への書き込みで恐縮ですが、
自分も同じような質問をしたことがあります。
結論としては「C++ではできない」でした。
http://hpcgi1.nifty.com/MADIA/Vcbbs/wwwlng.cgi?print+200907/09070015.txt
「なぜそんなことを?」とも指摘していただきましたが、
同じように疑問を持たれたり過去に調査されたかたもいらっしゃいました。
上記の書き込みにある
> CCCCがありがちな名前の場合
> BBBB_CCCCみたいな変な名前にしたくないんだよな。
というのと同じ心境ではないでしょうか。
(それとは違うことであればすみません)
概ね他の方に同意します。昔糞プロジェクトで
クラスBのなかでクラスAを使いたいがBのヘッダでAをインクルードすると
何かが多重定義で動かないって場合に
//h
class CBase;
class CHoge
{
public:
CHoge(void);
~CHoge(void);
CBase* pBase;
};
//cpp
#include base.h
CHoge::CHoge(void)
{
pBase = new CBase;
}
とかやったことがありますが。要はヘッダに実体が必要ない書き方にすれば場合によっては
動かせるかもしれない。が、猛者である皆様が指摘しないというとは推奨しないやり方な
のは間違いが無いし
内部クラスの話とは別。スレ汚し失礼しました
ひる様からのご回答により
「内部クラスの前方宣言はできない」ということがとりあえず
わかりましたので解決とさせていただきます.
皆様のおっしゃるように,確かに
わざわざ内部クラスにする必要もない話かと思うに至りました.
で,
>どちらにしてもその構造体に設定をする必要があるわけで
>かえって手間が増えるだけ
なる点についてですが,これもそれほどまともな理由があるわけでなく,
たくさんの値を一度に与えて設定を行わねばならないときに,
それを素直にたくさんの引数をもつメソッドとして書く場合に比べて
設定用構造体を挟む場合には構造体のコンストラクタを利用できるので
設定するコードがやや楽(柔軟?)ではないか?という…
SettingMethod( int a, double b, bool c, ... )
という形ではデフォルト引数値を使えるのは後ろ側の引数だけ
↓
構造体を引数に取る場合,明示的に構造体に代入した設定値以外はデフォルト使用
あと,設定処理は単純に渡されてきた値のコピーをクラスインスタンスに持たせるだけ
とは限らないわけで,
設定に使う値群→クラスの内部状態 が非可逆な場合などには
同じ設定処理を再度行いたいとかいう場合には「設定に使う値」型があると
良いかと思うのですが,ばかげているでしょうか…
結局,構造体に代入するんだからあまり変わらないかな?
> 同じ設定処理を再度行いたいとかいう場合には
CClass(const CClass& obj)
{
// ここにクローン処理
}
まとめて代入するとか,代入時に特殊な処理があるなら,
コピーコンストラクタ作っちゃえばいいんじゃないかな?
どうですか?
確かに既に設定されたインスタンスがある場合限定なら
コピーコンストラクタや operator= で対応できますが
めんどくさい設定情報って大抵 保存/読込 とかしたくなるので…
inner_class は主に隠蔽などの目的に使われるから、公開には積極的でない人が
俺も含めて多い、のだと思う。
ただこの例の場合、一番外側の「クラス」を「名前空間」にしてしまえば
みな(俺も含めて)違和感感じないのではないかな・・・とか思ったり。
namespace hogehoge { // hogehoge 機能を実装する「名前空間」
// 内部で使っている作業クラス
class many_member_setting_info { ... };
// 作業クラスを使い hogehoge 機能を主に実装するクラス
class implementation { ... };
// その他クラスや関数や変数やもろもろ必要なもの
...
}
名前空間の直下には public private など書けないけど