Viewの継承方法

[戻る]
あらら 2005/09/11(日) 19:11:57
はじめまして、あららと申します。

開発環境:Win2000 VC6.0SP2 MFC

CBaseViewに2つのViewのメンバ変数を持たせようとしたのですが、コンパイルエラー
が出てしまい、回避方法がわかりません。どなたか教えていただけないでしょうか。

<ファイル名:BaseView.h>
class CBaseView:CView{
    CLeftView  m_LeftView;
    CRightView m_RightView;
}
<ファイル名:LeftView.h>
class CLeftView : public CListView, public CBaseView
{
}
<ファイル名:RightView.h>
class CRightView : public CFormView, public CBaseView
{
}

ヘッダファイルのインクルードの有無、クラスの前方宣言、コンストラクタをprotected→
publicに変更、などを試したのですが、以下のようなエラーが出てしまいます。

error C2385: 'CLeftView::new' の派生があいまいです。
error C2504: 'CLeftView' : 定義されていない基本クラスが宣言されています。

初歩的な質問だと思いますが、よろしくお願いいたします。

Ban 2005/09/11(日) 21:34:15
MFC のクラスということはまず置いておくとして。

たとえば、CLeftView のサイズ(sizeof CLeftView)はいくつになると思いますか。

CLeftView は CBaseView を継承してますから、CBaseViewのサイズを含みますよね。

一方、CBaseView は CLeftView を持っているので、CBaseView は CLeftView の
サイズが必要ですよね。

提示コードは CLeftView のメンバに、CLeftView 自体を(間接的に)持とうとしてます。
少なくとも、CBaseView が持つのはポインタなどにしないと、互いに入れ子状態です。
前方宣言だけでは回避できません。

あらら 2005/09/11(日) 21:58:37
ご回答ありがとうございます。

> CLeftView は CBaseView を継承してますから、CBaseViewのサイズを含みますよね。
> 一方、CBaseView は CLeftView を持っているので、CBaseView は CLeftView の
> サイズが必要ですよね。
たしかにそのとおりですね。今、参考書などで確認いたしました。

> 少なくとも、CBaseView が持つのはポインタなどにしないと、互いに入れ子状態です。
やはり、そういうことなのですね。(いくら試してもダメなわけですね)

MFCでは、基本Viewクラスから継承したサブViewクラスを定義すること自体が不可能
なのでしょうか。

クライアントからの指示:
1:MDI(3つのチャイルドウィンドウ)
2:各チャイルドウィンドウは分割画面(縦2分割:上段はリストビュー、下段は
    フォームビューが2種類)
3:上段画面で選択したリストの情報により、下段のフォームビューの表示を切り替える
4:メニュー処理はチャイルドフレームではなく基本Viewクラスを作成し、
    その基本Viewクラスで行うこと。

上記の条件が提示されているので、ご質問させていただきました。(事後報告になって
しまい、申し訳ありませんでした。)

当方、上記の3について、チャイルドフレームかメインフレームにて処理を実装させて
ほしいと言ってみたのですが、以下の理由により上記の3にて開発しろとのことになって
しまいました。
(理由)
a.オブジェクト指向にのっとる(共通部分をクラス化すること)
b.チャイルドフレームは、画面の枠(フレーム)を管理するクラス

ヒントでもかまいませんので、実現する方法などのアドバイスをいただけますでしょうか。
よろしくお願いいたします。

あらら 2005/09/11(日) 22:07:56
申し訳ありません、誤記がありました。

誤)当方、上記の3について、チャイルドフレームかメインフレームにて処理を実装させて
正)当方、上記の4について、チャイルドフレームかメインフレームにて処理を実装させて

以上となります。

また、下記の方法を実行したのですが、実体を持つことと同様のコンパイルエラーが
出てしまいました。
> 少なくとも、CBaseView が持つのはポインタなどにしないと、互いに入れ子状態です。

根本的に、私の考え方が間違っているのでしょうか。
どのようなことでもかまいませんので、ぜひ、よろしくお願いいたします。

Ban 2005/09/11(日) 22:17:41
まずは、また MFC のクラスということは置いておいて。
CBaseView が CView のサブクラス、
CListView も CView のサブクラス。
同一クラスのサブクラスを多重継承するクラスを作る場合、
C++ としては「仮想継承」することになるかと思います。
そして、両者が同名のメンバ関数を持つなら名前の解決が必要です。

そしてMFCの話ですが、

> 基本Viewクラスから継承したサブViewクラスを定義すること自体が不可能なのでしょう
か。

単体でViewを継承するのは可能ですし、そうやって利用される設計です。
ただし、仮想継承や多重継承を前提には設計されてません。


これを踏まえてアドバイス(?)など。

・基本View の継承は、本質的なものか。単なる実装継承ではないのか。
・基本View なるものは、View を継承する必要が本当にあるのか。ただのクラスにできな
いか。
・基本View なるものを各View が継承する必要があるのか。委譲できないか。

Ban 2005/09/11(日) 22:28:24
> a.オブジェクト指向にのっとる(共通部分をクラス化すること)

これは、あまりオブジェクト指向的ではない考え方だと思うのですが。

「実装が共通だからそのクラスを継承」という発想であれば、
それは「実装継承」と言われるもので、オブジェクト指向としては、
忌避される傾向にあると思います。
多重継承できないオブジェクト指向言語も多いですしね。

あらら 2005/09/12(月) 10:03:51
Banさん、アドバイスありがとうございます。

※頭が混乱してしまい、質問の意図が分かりづらくなってしまったので、1つずつ
  解決していこうと思います。よって、
4:メニュー処理はチャイルドフレームではなく基本Viewクラスを作成し、
    その基本Viewクラスで行うこと。
を対象とさせてください。

> ・基本View の継承は、本質的なものか。単なる実装継承ではないのか。
> ・基本View なるものは、View を継承する必要が本当にあるのか。ただのクラスにで
> きないか。
> ・基本View なるものを各View が継承する必要があるのか。委譲できないか。
以上のアドバイスから、以下のように変更してみました。しかし、うまく動作しま
せん。

<ファイル名:BaseView.h>
class CBaseView:CView{
}
<ファイル名:LeftView.h>
class CLeftView : public CListView{
}
<ファイル名:RightView.h>
class CRightView : public CFormView{
}

<実現方法>
1:メニューリソースにCBaseViewを関連付ける。
2:APPクラスのInitInstance内にて、ドキュメントテンプレートにはCBaseViewを
    指定する。
3:分割ウィンドウに、CLeftViewとCRightViewを左右に作成する。
4:CBaseViewにメニューの実装をする。

とやったのですが、メニューがアクティブになりませんでした。おそらく、CBaseView
がアクティブではないためだと考えられます。
やはり、メニュー処理はチャイルドフレームで行うのが妥当だということなので
しょうか?

あらら 2005/09/12(月) 10:18:20
Banさん、たびたびありがとうございます。

> > a.オブジェクト指向にのっとる(共通部分をクラス化すること)

> これは、あまりオブジェクト指向的ではない考え方だと思うのですが。

> 「実装が共通だからそのクラスを継承」という発想であれば、
> それは「実装継承」と言われるもので、オブジェクト指向としては、
> 忌避される傾向にあると思います。
> 多重継承できないオブジェクト指向言語も多いですしね。
なるほど、このことについてさらに調べてみようと思います。
※VC歴5ヶ月、オブジェクト指向設計歴1ヶ月なので、両方ともあまり詳しくない
  ので、質問自体があいまいになってしまい、申し訳ありません。

PATIO 2005/09/12(月) 10:33:19
<ファイル名:BaseView.h>
class CBaseView:CView{
}
<ファイル名:LeftView.h>
class CLeftView : public CListView{
}
<ファイル名:RightView.h>
class CRightView : public CFormView{
}

これだと、CLeftView、CRightViewともにCBaseViewからは派生していないと言う事に
なりますけれど、その点はお分かりですか?
継承関係をつなげるためには、

CView -> CBaseView -> CLeftView
CView -> CBaseView -> CRightView
として、CListViewとCFormViewに相当する機能は自分で実装するしかないと思います。
どうしても単一継承でこれをやるというのであれば、CBaseViewから派生した
CListViewやCFormViewに相当するものを自作するとかしないと駄目なのでは?
今後も同じクラスでViewクラスを作成する予定があるのであれば、無駄とは思いませんが、
工数的にはかなりかかると思います。もし、MFCが提供しているCListViewやCFormViewと
同じ使い勝手を要求されているならなおさら面倒そうですし。

PATIO 2005/09/12(月) 10:38:55
あと、MFCを使うのであれば、MFCが想定した使い方でないとかえって効率が落ちると思い
ます。
CListViewやCFormViewを使いたいのであれば、メニュー関連の操作をやってくれるクラスを
自作してそのクラスを派生先のクラスのメンバー変数として実装した方が楽なのでは?
各Viewのハンドラ内ではそのクラスのメンバー関数を呼び出すようにすれば、処理は一本化
出来そうです。
但し、自動で呼び出されるというわけではないので自分で呼び出す必要が出てきますけれど。

Ban 2005/09/12(月) 10:45:40
CBaseView を、単なる「メニュー管理用クラス」にする方が現実的だと思います。
あとは、それを実装継承で多重継承するか、メンバで持って委譲するかは設計次第でしょ
うか。

> 4:メニュー処理はチャイルドフレームではなく基本Viewクラスを作成し、
>    その基本Viewクラスで行うこと。

基本View が CView を継承しろとか、基本Viewを継承しろとか言う制限はない模様。
単にメニュー処理が「基本View」なるクラスで一括処理されていれば要件を満たすと理解。

# メニューの管理で「何をするのか」にもよりますが、
# お話を読んでいる限りでは根本的にMFCやらOOをわかっていない要求のような気も...。

Ban 2005/09/12(月) 10:47:33
# PATIO さん(の2つめ)とかぶりました。
# まぁそんな感じで説得してみてはどうでしょう。

あらら 2005/09/12(月) 12:02:25
[[解決]]
Banさん、PATIOさん、ご回答ありがとうございます。

まさにお二人の言っていることが、私の疑問点そのものだったのです。
(※申し訳ありません、うまく説明できなくて...)

> CListViewやCFormViewを使いたいのであれば、メニュー関連の操作をやってくれるク
> ラスを
> 自作してそのクラスを派生先のクラスのメンバー変数として実装した方が楽なので> 
> は?
> 各Viewのハンドラ内ではそのクラスのメンバー関数を呼び出すようにすれば、処理は
> 一本化出来そうです。
> 但し、自動で呼び出されるというわけではないので自分で呼び出す必要が出てきます
> けれど。
> 単にメニュー処理が「基本View」なるクラスで一括処理されていれば要件を満たすと
> 理解。
アドバイスを元に、「基本View」というクラスを自作する方向でやってみます。

前提条件:
1:基本Viewクラスをチャイルドフレームのメンバとする。
処理の流れ:
1:ユーザがメニューを操作
2:チャイルドフレームが検知し、基本Viewクラスの該当するメンバ関数を呼ぶ。
    (振り分け処理)
3:基本Viewクラスのメンバ関数内にて個々の処理を行う。

という流れで進めようと思います。

Banさん、PATIOさん、私の至らぬ説明にお付き合いして頂き、ありがとう
ございました。クライアントには、上記の方法で良いかを問い合わせてみます。

表題の解決ではないのですが、アドバイス頂いた方法で交渉する、ということで
一応の解決としたいと思います。また解決できない問題が出たときには、宜しく
お願い致します。

古いスレッドには追加発言できません。