• ベストアンサー

親クラスのフリをした子クラスの使い方を教えて下さい

以前、QNo.3479089でお世話になった者です。類似というか、続きの質問です。 ClassAを継承するClassBがあり、条件によって、ClassAまたはClassBをインスタンス化して使います。 (ClassBは、ClassAのメソッドの一部をオーバーライドして、ClassB固有のメソッドも追加しています) ClassA* pObj = NULL; if( ある条件 ) {   pObj = new ClassA(); } else( その他の条件 ) {   pObj = new ClassB(); } pObj->common_method(); これが常套手段と伺ったのですが、ClassBがインスタンス化された場合、ClassB固有のメソッドmethodBを呼び出そうとすると、「ClassAにはmethodBが存在しない」といったようなコンパイルエラーが出ます。 pObj->methodB(); // ここで、エラー それで、エラーを解消するために、「その他の条件」の時に、dynamic_castを使ってみました。 if( その他の条件 ) {   ClassB* pObj2 = dynamic_cast<ClassB*>( pObj );   pObj2->methodB(); } 上記でコンパイルエラーは解消したのですが、なにか、非常にめんどくさいです。 また、ClassAのフリをした実体ClassBを使っているのだから、オーバーライドしているメソッドもdynamic_castをしないと呼び出されないような気がします。 結局、親のフリした子クラスを作成する場合、こんなにめんどくさいことをしないといけないのでしょうか? それともこのやり方はおかしいですか? この場合、どういうふうにするものなのか、教えて下さい。よろしくお願いします。

  • aneja
  • お礼率93% (379/405)

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

> それなりに面倒なのはアタリマエということは、質問の方法で間違っていない、ということですか? YES. だからこそ、そんなことせずに済むような設計/実装を考えるが上策です。 それでもなお、やむを得ぬときにやる分にはかまいません。 頻繁にやらねばならんなら、設計を見直しましょう。

aneja
質問者

お礼

再度のご回答、ありがとうございます。 実装の段階になってこのようなことに気がついてしまったので、経験を積んだ方なら、初めからこんな設計にしなかったかも知れません。。 今後の参考にさせていただきます。ありがとうございました。

その他の回答 (4)

  • rinkun
  • ベストアンサー率44% (706/1571)
回答No.5

> また、ClassAのフリをした実体ClassBを使っているのだから、オーバーライドしているメソッドもdynamic_castをしないと呼び出されないような気がします。 これはC++言語の欠陥ですが、C++ではオーバーライドする予定のメソッドは親クラスでvirtual宣言しておく必要があります。 virtual宣言されたメソッドの場合、親クラスのポインタから呼び出しても実体が子クラスなら子クラスでオーバーライドしたメソッドが呼ばれます。 それで、子クラスでの追加処理というのはどのような文脈で呼んでいますか。親クラスでの通常処理に追加する形で呼ぶなら本質的には親クラスの該当メソッドをオーバーライドして追加処理する形が適当でしょう。 そうでないなら設計に問題がある可能性が高いですね。本当は親クラスでも同様の処理(ただし親クラスでは空処理で良い)が必要だとか。

aneja
質問者

お礼

ご回答、どうもありがとうございます。 virtual宣言は、こういうためにあるのですね。今まで実際の使い方がピンと来ませんでしたが、ご説明でよくわかりました。 子クラスでの追加処理は、共通処理の中で、「classBがインスタンス化される条件」の時のみ呼び出しています。 仰るとおり、親クラスでも空処理を定義すると、条件分岐がいらないし、virtual宣言しておけば、ダウンキャストも必要ないですね。 だいぶん、すっきりしてきました。ありがとうございました。

  • hidebun
  • ベストアンサー率50% (92/181)
回答No.4

「classBをインスタンス化するときの条件分岐」と 「メソッド呼び出し時の分岐条件(その他の条件)」は、 等価なものですか? そうであれば、もともとclassBにclassAのフリをさせることに無理があるように思います。 コードは、異なる処理を無理やり1つの関数に突っ込んだ、という感じになってしまうでしょう。 そうなっても相応のメリットがあれば、上記のような方式をとるのも仕方がないですが、 質問者さんの勘がNGと言っているのですよね? そういう場合、10中8,9、後から見ても、「とってつけたようなコード」にしか見えないことでしょう。 逆にメリットが極大であれば、このような細かいことは気にならないものです。

aneja
質問者

お礼

ご回答、どうもありがとうございました。 >「classBをインスタンス化するときの条件分岐」と >「メソッド呼び出し時の分岐条件(その他の条件)」は、 >等価なものですか? classBをインスタンス化するときの条件分岐と、classB独自のメソッド呼び出し時の分岐条件(その他の条件)は同じです。 ただ、ANo.2の方のお礼にも書いたとおり、classAとclassBはほとんどが同じ処理で、classBは、特別な条件の時に独自の処理を追加したもの、という感じです。 自分は、オブジェクト指向をまだまだよく理解していないので、一般的にみれば、メリットの方が大きいのかもしれません。

回答No.2

 ClassBを基底クラスの ClassAとして扱っている以上、ClassB独自のメソッドを 呼ぶのはちょっとイリーガルです。  claasA/classB、各メソッドの役割など詳細がわからないので、 正しいことはわかりませんが,クラスの設計かこの方法か 何かが間違っているのではないでしょうか。

aneja
質問者

お礼

ご回答、どうもありがとうございます。 classA/classBの役割というのは、通常はほとんどの処理が同一で、classBは、特別な条件の時に独自の処理を追加したもの、という感じです。 >クラスの設計かこの方法か >何かが間違っているのではないでしょうか。 クラス設計は、今作ろうとしているモノの仕様を熟知していて、かつオブジェクト志向設計のキャリア十数年の人のレビューを受けて、こうなったものです。方や実装した自分はC++一年目ですので、実装がオカシイということなんでしょうね。

回答No.1

何らかの目的があって基底クラスのフリをさせているんだから、 導出クラスで定義されたメソッドを呼ぶにはそれなりに面倒なのはアタリマエかと。そうでなくちゃ困るし。

aneja
質問者

お礼

早速のご回答ありがとうございました。 それなりに面倒なのはアタリマエということは、質問の方法で間違っていない、ということですか?

関連するQ&A

  • 無名パッケージのクラス

    パッケージ化されたクラス(pacA.ClassA)から、無名パッケージ(ClassB)のクラスを参照できるのでしょうか。 状況を詳しく書きますと、 ClassB はコンパイル済みで、pacA.ClassA の中で ClassB を new しています。 pacA.ClassA をコンパイルしたいのですが、ClassB を import できません(コンパイルエラー)。 それとも、ClassB は import する必要がなく、単に私のコンパイルの仕方(classpathの通し方等)が悪かったのでしょうか。 どなたか教えて頂けませんか。

    • ベストアンサー
    • Java
  • [Obj-c]元クラスからサブクラスのメソッド

    Objective-Cの勉強をしています。 ClassAは元クラス(スーパークラス)です、 ClassB1、B2はClassAを継承(サブクラス)しています。 ClassAとClassB1にはiMethodというメソッドが存在します。 最下部にソースと実行結果があります。 ●そこで質問なのですが、  ClassAのインスタンスにClassB1のインスタンスをセットすると、  同じ名前のメソッドがあるだけでなぜClassB1のメソッドが呼ばれるのでしょうか?  (下記ソースの[bangai iMethod]; の部分です。)  ClassB1のインスタンスを渡すとClassAのインスタンスが  ClassB1のメソッドを使用できる理由がわかりません、  継承しているとはいえ型が違うものを参照渡しできる理由も今ひとつわからないです。  またこれはどういう機能を言うのでしょうか?  (例えば ポリモーフィズム、動的バインディングなど) ★ソース ------------------------------------------------------------------------------ #import <Foundation/Foundation.h> // ClassA @interface ClassA : NSObject { } @end @implementation ClassA -(void) iMethod { NSLog(@"スーパークラスのインスタンスメソッドです。\n"); return; } @end // ClassB1 @interface ClassB1 : ClassA @end @implementation ClassB1 -(void) iMethod { NSLog(@"サブクラスのインスタンスメソッドです。\n"); return; } @end // ClassB2 @interface ClassB2 : ClassA @end @implementation ClassB2 @end int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... ClassB1 *instance_B1 = [[ClassB1 alloc]init]; ClassB2 *instance_B2 = [[ClassB2 alloc]init]; [instance_B1 iMethod]; [instance_B2 iMethod]; // 親には子のクラスが入れられる ClassA *bangai = instance_B1; [bangai iMethod]; } return 0; } ------------------------------------------------------------------------------ ★実行結果 サブクラスのインスタンスメソッドです。 スーパークラスのインスタンスメソッドです。 サブクラスのインスタンスメソッドです。

  • 条件によって別のクラスのインスタンスを作成する場合

    大きな条件分岐があって、その条件によって別のクラスを同じ名前でインスタンス化し、以降で使いたいのですが、うまくコンパイルが通りません。考えてみればifブロックの中だけで有効なインスタンスになってしまうので、当たり前かも知れません。どのようにすれば、使えるようになるのでしょうか。 以下にコードを載せます。正しい書き方を教えて下さい。 よろしくお願いします。 if(条件A){ classA object; // objectという名前でclassAをインスタンス化 }else{ classB object; // objectという名前でclassAとは別のclassBをインスタンス化 } object.methodA(); // objectのmethodA関数を使用したい

  • (クラス名.this.メソッド)って・・・?

    次のようなクラスで public class ClassA {   public static void main(String[]args)   {     new ClassA(); /* 構築A */   }   public ClassA()   {     new ClassI();   }   public class ClassI()   {     ClassA.this.MethodA(); /* 命令A */   }   public void MethodA()   {     System.out.println("HELLO");   } } この命令Aの部分の (クラス名.this.メソッド)というアクセス方法がよくわかりません とりあえす(ClassA.this)が構築Aの部分で生成された インスタンスではないかとおもうのですがそれで正しいのでしょうか? また インナークラスはインナークラスの定義されているクラス以外から インスタンスを生成できないのでしょうか? つまり次のクラスを追加して public class ClassB {   public ClassB()   {     new ClassA.ClassI(); /* 構築B */   } } ここの構築Bのように(この場合はダメの様ですが)他のクラスから 構築することです もし仮にできるとしたら そのときの命令Aの(ClassA.this)は一体なんの インスタンスを指すのでしょうか 急ぎのプログラムを作っているので たいへん不躾ですが、なるべく早くお答えをお願いします

    • ベストアンサー
    • Java
  • 参照メンバを持つクラス

    ClassAとClassBにCMyDataオブジェクトを渡したいのですが、メモリ上に同じデータのコピーを作りたくないので、コンストラクタがCMyDataの参照を受け取る設計にしました。こうすることで、CMyDataの実体はメモリ上に1つしか存在しないことになります。 ただ、クラスが、自身が管理していない外部領域にあるデータへの参照を使って仕事をするというのに違和感を感じます。 完全コンストラクタというデザインパターンがあって、クラスのインスタンスを生成する時に必要な全てのデータを渡すのが正しい流儀だそうです。 しかしこのClassA(B)の設計では、ClassA(B)の寿命が終わるまで参照先のデータが生きているとは限りません。もっといい設計はないでしょうか?ちなみにCMyDataは実際にはかなり大きなデータで、一時的であってもコピーを複数持ちたくないのです。ClassAとClassBは、実際は他のデータも参照するため1つのクラスにはしたくないのです。 class CMyData { // 色々なデータ }; class CMain { CMyData* myData; // ファイルからデータを読み込んで生成 ClassA* objA; ClassB* objB; void ReadFile() { myData = new CMyData(); // ファイルからデータを読み込みCMyData構築 } void Init() { ReadFile(); objA = new ClassA(*myData); // 自身のメンバ変数の参照でClassAを構築 objB = new ClassB(*myData); } void DoProcess() { Init(); // 実行は一度だけ objA->hoge(); // ClassAに仕事をさせる objB->foo(); // ClassBに仕事をさせる } } class ClassA { CMyData& m_data; public: ClassA(CMyData& data) m_data=data; // CMyDataオブジェクトを参照で受け取る void hoge(); } class ClassB { CMyData& m_data; public: ClassB(CMyData& data) m_data=data; // CMyDataオブジェクトを参照で受け取る void foo(); }

  • 同一クラスインスタンス名で別クラスのインスタンス作成方法(C++)

    下記ソース(Java)の処理をするような、C++の実装方法を教えていただきたいです。 【処理内容】 クラスBのインスタンスを保持しており、クラスBのインスタンス名と同一であるクラスAのインスタンス生成 ClassA A_Instance = (ClassA)Class.forName(B_instance.name).newInstance(); 要はクラスインスタンスの名前の求め方がわからないのです。それさえわかれば、newしてクラスポインタを返すメソッドを用意すれば何とかなると考えていますが。 ※C++のAPI一覧はどこにあるのだろうか・・・

  • JAVAにおけるメモリリーク

    クラスAの中でクラスBがnewされているとき、 クラスAの参照をnull にセットしさえすれば、 内包されているクラスBに確保したメモリも即時に解放されるのでしょうか? それともガーベジコレクションにてどこからも参照されていないことがわかった時点で解放されるのでしょうか? はたまた解放されないのでしょうか? ClassA {  ClassB instanceX = new ClassB(); } - - - - - - - - - - - - - - - - - - - - - - - - new をした際、そのメソッドの最後や、 Exceptionのキャッチ後のthrow前に、 変数 = null; は書いていますが、 これ以外ではどういう点に気をつければ良いかがわかっていません。 どういうときにJavaでメモリリークが起こるのでしょうか?

    • ベストアンサー
    • Java
  • 宣言していないクラス

    畏れ入ります。 例外処理の勉強をしています。 例1の場合は class CarException extends Exception{ } とクラスを定義してから if(g<0){ //例外の条件です CarException e = new CarException(); throw e; としていました。 しかしふと、ほかの例題を見てこのように変えてみました /*クラス宣言削除/ if(g<0){ throw new CarException(); } これでもまったく同じ結果でした。 わからないのは、なぜこの場合には CarExceptionクラスの定義がないのに ”シンボルが解決できません” のコンパイルエラーにならないのでしょうか? newがついているから・・・というのはわかるんですが・・・ 初歩的な質問です、すみません

    • ベストアンサー
    • Java
  • 「Javaのクラスメソッド内でsuperキーワードは使える?」

    「Javaのクラスメソッド内でsuperキーワードは使える?」 Java言語仕様(第2版日本語訳初版)(注)の以下の記述をどう解釈するべきでしようか?  「8.4.6継承、オーバーライド、隠蔽」>「8.4.6.2隠蔽(クラス・メソッドによる)」において、staticメソッドがシグネチャが同じスーパークラスのメソッドを隠蔽し、それがインスタンスメソッドであればコンパイルエラーになる、との記述のあと、下記のような記述があります。 『隠蔽されたメソッドは、限定名、あるいはsuperキーワードやスーパークラス型へのキャストを含んだメソッド起動($15.12)を使用することによりアクセスすることができる』 まるで、「インスタンンスメソッドと同じようにクラスメソッドにおいてもsuperキーワードを使用することでスーパークラスのstaticメソッドを呼び出せる」ように読み取れます。 ちなみに該当部分の原典は、  http://java.sun.com/docs/books/jls/download/langspec-2.0.pdfから抜粋  8.4.6.2 Hiding (by Class Methods) (略)  A hidden method can be accessed by using a qualified name or by using a  method invocation expression (§15.12) that contains the keyword super or a cast  to a superclass type. In this respect, hiding of methods is similar to hiding of  fields. また、第3版原典も該当部分は同じ記述でした。  http://java.sun.com/docs/books/jls/download/langspec-3.0.pdf  8.4.8.2 Hiding (by Class Methods) なお、superキーワードについては、同書 15.11.2 「superを使用したスーパークラスのメンバへのアクセス」の項で、 『superキーワードを使用した特殊形式は、インスタンス・メソッド、インスタンス初期化子、コンストラクタ、あるいはクラスのインスタンス変数の初期化子中でのみ有効となる』 とあり、クラスメソッド中では使えないと読み取れます。(続けて、 superとthisの使用条件は全く同じ、としています。) Javaコンパイラは、クラスメソッド中のstaticキーワードを認識せず、エラーとします。 諸兄のお考えをお聞かせください。 注)http://www.amazon.co.jp/Java%E8%A8%80%E8%AA%9E%E4%BB%95%E6%A7%98-Java-%E3%82%B8%E3%82%A7%E3%83%BC%E3%83%A0%E3%82%BA-%E3%82%B4%E3%82%B9%E3%83%AA%E3%83%B3/dp/4894713063/ref=sr_1_2?ie=UTF8&s=books&qid=1270203134&sr=1-2

  • Javaコレクションフレームワークの、クラス型とインタフェース型の使い分けについて

    Javaコレクションフレームワークの、クラス型とインタフェース型の使い分けについてですが、 newで作ったインスタンスを、 クラス型に代入する場合と、インタフェース型に代入する場合があるのですが、どちらを優先して使ったほうがいいですか? ※物によっては、インタフェース型しかつかえないとか、どちらでも良い場合とかいろいろあると思います。 また、インタフェース型のほうがメソッドの種類が若干多いですが、抽象メソッドなのでオーバーライドして使うからそれは関係ないのかな。 ・本に出てきた例 ArrayList<E> list = new ArrayList<E>(); List<E> list = new ArrayList<E>(); Queue<E> queue = new LinkedList<E>(); LinkedList<E> list = new LinkedList<E>(); LinkedList<E> stack = new LinkedList<E>(); Set<E> set = new HashSet<E>(); Map<E, M> map = new HashMap<E, M>();

    • ベストアンサー
    • Java

専門家に質問してみよう