• ベストアンサー
  • 困ってます

継承したクラスを、継承元のクラス型の引数に渡すとどうなるのでしょうか?

継承したクラスを、継承元のクラス型の引数に渡すとどうなるのでしょうか? 以下のようなケースで、 #include "stdio.h" using namespace std; // baseクラス class base { private:  int m_nII;  int m_nJJ;  int m_nKK; public:  base(int i,int j,int k){ m_nII=i; m_nJJ=j; m_nKK=k; }  int GetSum(){ return (m_nII+m_nJJ+m_nKK); } }; // base 継承クラス class hoge : public base { public:  hoge() : base(1,2,3){} }; void func(base* obj){ // baseクラスを引数に取る関数  printf("sum is %d\n", obj->GetSum()); } // main int main(){  hoge objHoge;  func((base*)&objHoge); // <-キャストして渡す  return 0; } として、一応、gccでコンパイルは通り、実行結果も期待通りだったのですが、 このやり方で問題は無いのでしょうか? (たとえば継承先のクラスが独自のメンバを持っていたりなどした場合、期待した結果にならないとか・・) よろしくお願いします。

共感・応援の気持ちを伝えよう!

  • 回答数2
  • 閲覧数1029
  • ありがとう数2

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

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

言語仕様通りで問題ありません。 実行時の挙動の話をすると、 1.プログラムは関数と変数に分けられてメモリ上に配置される プログラムがメモリにロードされて命令が実行される段階では、プログラムはテキスト領域、静的領域、ヒープ領域、スタック領域に分けられて実行されます。 テキスト領域には、関数が機械語に翻訳された内容が格納されますので、このことからメンバ関数を増やしても問題になりません。 2.メンバ変数は基底(継承元)クラスのメンバの後に継承クラスのメンバが継ぎ足しされる。 hoge のインスタンス(obj)のメモリ上の配置 1000から1019のアドレスにobjは格納される。 1000から1011のアドレスにobjのbaseクラスのメンバが格納される。 1012から1019のアドレスにobjのhogeクラスのメンバが格納される。 1000: base.m_nII; // base クラスのメンバ変数(obj) 1004: base.m_nJJ; // 〃 1008: base.m_nKK; // 〃 1012: hoge.m_anpan; // hoge クラスで追加したメンバ変数 1016: hoge.m_pizza; // 〃 3.メンバ関数は、構造体へのポインタを受け取る関数 「func」関数の引数で「base *obj」を指定し「obj->GetSum()」と呼び出すことは 「static int base::GetSum(base *obj)」の呼び出しと同じである。 つまり、構造体へのポインタを引数とする関数に等しい。 // 以上のことをC言語に直すとこんな風になります。 struct base { int m_nII, m_nJJ, m_nKK; }; struct hoge { struct base base; int m_anpan, m_pizza; }; void base_init(struct base *obj, int i, int j int k) { obj->m_nII = i; obj->m_nJJ = j; obj->m_nKK = k; } int base_GetSum(struct base *obj) { return obj->m_nII + obj->m_nJJ + obj->m_nKK; } void hoge_init(struct hoge *obj) { base_init((struct base *)obj, 1, 2, 3); obj->m_anpan = 120; obj->m_pizza = 3000; } int hoge_GetPizza(struct hoge *obj) { return obj->m_pizza; } int main() { struct hoge obj; hoge_init(&obj); printf("sum is %d\n", base_GetSum((struct base *)&obj)); obj.m_anpan = 1200; printf("Anapn is \\%d\n", obj.m_anpan); printf("Pizza is \\%d\n", hoge_GetPizza(&obj)); // アドレス表示 printf("obj.base.m_II : %x\n" &obj.base.m_II); printf("obj.base.m_JJ : %x\n" &obj.base.m_JJ); printf("obj.base.m_KK : %x\n" &obj.base.m_KK); printf("obj.m_anpan : %x\n" &obj.m_anpan); printf("obj.m_pizza : %x\n" &obj.m_pizza); return 0; } ポイントは、クラスという概念は関数と構造体を一体的に扱う仕組みであり言語仕様上にのみ存在し、実行時にメンバ関数は単純な引数渡しと関数呼び出しになるということです。 関連として、関数のオーバーライド、ポリモルフィズムなどがありますが、それの実体は構造体のレコードに関数ポインタ配列へのポインタを持たせているだけです。 つまり理解するには、C言語の関数ポインタ、関数ポインタの配列について理解することが、遠回りですが近道になります。 # 理解しなくても使うことはできますが(ーー;) あと、コードっぽいのは適当に書いただけなので間違いがあると思います。

参考URL:
http://brain.cc.kogakuin.ac.jp/~kanamaru/lecture/MP/final/part06/node8.html

共感・感謝の気持ちを伝えよう!

質問者からのお礼

内部的なことまで理解でき、大変感謝です。 ありがとうございました。

関連するQ&A

  • テンプレート引数の型推測

    コンパイラはVC++2008です。 いろいろあって、あるクラスにおいて関数ポインタと関数オブジェクト双方を 同じように利用できないかと考えて、次のように試みました。 class Base { public:     virtual void func() =0; }; template<class Func> class CFunc :public Base { private:     Func m_func; public:     CThreadFunc(Func func):m_func(func){}     void func(){m_func();} }; class Hoge { private:     Base* base; public:     template<class Func>     Hoge(Func func)       :base(new CFunc<Func>(func))     {}     ~Hoge()     {       delete base;     }     void DoSomething()     {       base->func();     } }; クラスをテンプレートにするといちいち指定しなければならないので、 まず基底クラスに適当な仮想関数を設け、それを継承したクラスをテンプレートにしました。 そしてコンストラクタの引数で何かしらを受け取って、オーバーライドした関数の中で 関数ポインタか関数オブジェクトだと仮定して呼び出しています。 さらに基底クラスのポインタを目的のクラスが保持してやり、 こちらはコンストラクタをテンプレートにすることで引数から型を推測してもらうことで 先ほどのテンプレートクラスのインスタンスを作成しています。 そしてポインタを介してfunc()を使ったり…、などすれば、 とりあえず引数なしの関数と関数オブジェクトを同等に扱えないかなと思ったからです。 で、このようなクラスを作成してコンパイルすると、 void func(); //何かしら処理する関数 class Function { public:   void operator ()();  //何かしら処理する関数オブジェクト }; があったとして、 int main() {   Function function;   Hoge hoge(function); //いったん作ってから渡す   Hoge hoge2(func); //関数を渡す     hoge.DoSomething();   hoge2.DoSomething();    } は動きました。しかし、 int main() {   Hoge hoge(Function()); //引数を初期化する } とすると次のようなエラーが出ます。 warning C4930: 'Hoge hoge(Function(__cdecl *)(void))': プロトタイプされている関数が呼び出されませんでした (変数の定義が意図されていますか?) また、 int main() {   Hoge hoge(Function()); //引数を初期化する   hoge.DoSomething();  //クラスにアクセス } とすると次のようなほかのエラーが出ます。 error C2228: '.DoSomething' の左側はクラス、構造体、共用体でなければなりません。 しかし、例えば関数オブジェクトのコンストラクタに引数が設定されていたとして、 class Function { public:   Function(int dummy);  //何か値を受け取る   void operator ()();  //何かしら処理する関数オブジェクト }; となっていた時、 int main() {   Hoge hoge(Function(1)); //引数を初期化する   hoge.DoSomething();  //クラスにアクセス } の呼び出しは正常にコンパイルされ、想定通りの動きをします。 全く使わなくても、一つ以上の適当な引数を何でもいいからコンストラクタが持てば、 普通にコンパイルされるみたいです。ただ、デフォルト引数を与えてHoge hoge(Function())と 同じ形ですと引数があってもできないみたいです。 まったく通らないなら最初からあきらめるですが、中途半端にちゃんと動くために エラーの原因を知りたいと思っています。 テンプレートの場合には、引数に渡すタイミングで初期化はしてはいけないのでしょうか?

  • C#でクラス継承について

    下記のようなコードは、可能なのでしょうか? // クラス定義 abstract class Base { int a; Public int A { get{ return a; } } } class Sub1 : Base { int b; public int B { set { b = value; } } }; class Sub2 : Base { // 中略 } static class Program { [STAThread] static void Main(string [] args) { Base aaa = new Sub1(); Base bbb = new Sub2(); func(aaa); // ここでCS1502, CS1503 func(bbb); // ここでCS1502, CS1503 } void func(Sub1 _aaa) { // 中略 } void func(Sub2 _bbb) { // 中略 } } コメントに書いたとおり、CS1502(引数が合わない)が出ます。 func((Sub1)aaa); の様にキャストしないと、動作させることは難しいでしょうか? ※ 実際のコードでは、func(aaa), func(bbb)の呼び出し部分をループでまわしたいと考えています。

  • Perlの継承について

    Perlの継承について (1) クラス Subclass (baseclassを継承) myfunc1 (2) クラス Baseclass myfunc1 myfunc2 (myfunc2内でmyfuncを呼び出している) (3) SubclassとBaseclassのmyfunc1は、引数が異なる。 上記のようにクラスSubclassとクラスBaseclassがあり、SubclassはBaseclassを継承しています。 ここで、SubclassではBaseclassのmyfunc1をオーバーライドしています。 1: my $obj = Subclass->new(); 2: $obj->myfunc2(); とすると、2にて、Subclassにて実装したmyfunc1が呼び出されます。これを、Baseclassのmyfunc1を呼び出させる方法はないでしょうか? (Baseclassはその他のクラスからも継承されておりいじりたくない。 しかし、SubclassはBaseclassを継承したうえでmyfunc1をオーバーライドしたい。) Baseclassのmyfunc2内の  $self->myfunc1(...) という記載を、  Baseclass::myfunc1($self, ...) という記載にしてしまえば動きそうな気もするのですが、何かつっこみがあればお願いします。

    • ベストアンサー
    • Perl

その他の回答 (1)

  • 回答No.1
  • anmochi
  • ベストアンサー率65% (1332/2045)

 全く問題ないですよ。よく使われる手法です。C++には欠かせないテクニックで、ポリモーフィズムを実現する方法の一つですね。  継承先のクラスが同名の独自のメンバを持っている(今回はhogeクラスがint GetSum()を持っていた)場合に、どっちが呼ばれるかは、このソースではbaseの方になります。これを、hogeの方を呼ばせたければbaseのint GetSum()にvirtualというキーワードをつけます。  この辺の詳しいお話は「オーバーライド」や「仮想関数(virtual)」などをキーワードに検索してみてください。

共感・感謝の気持ちを伝えよう!

関連するQ&A

  • コンストラクタの記述について ―引数を持ったクラスを継承する場合―

    コンストラクタの記述について ―引数を持ったクラスを継承する場合― ActionScript3.0でプログラミングをしています。 クラスの継承に挑戦したのですが、うまくいきません。 コンストラクタの記述は、何か特別なものが必要なのでしょうか? ■コンストラクタに引数なし → 成功。 ■コンストラクタに引数あり → 失敗。  エラー「No default constructor found in base class <親クラスの名前>」。 ■エラーの出るソース↓ ・Main.as(コンストラクタ内抜粋)     var parent:Parent = new Parent(100);     var child:Child = new Child(200); ・Parent.as package {   import flash.display.Sprite;      public class Parent extends Sprite {     protected var str:String = "親クラスのプロパティ";     public function Parent(inNum:Number):void {       trace("親クラス, ", str, inNum);     }   } } ・Child.as package {   import Parent;   public class Child extends Parent {     public function Child(inNum:Number):void { //■エラー。       trace("子クラス, ", str, inNum);     }   } } ご存知の方、よろしくお願いします。 よろしくお願いします!

    • ベストアンサー
    • Flash
  • 個々の関数について継承しているかどうかを判定したいんですが、

    個々の関数について継承しているかどうかを判定したいんですが、 class CBase {public: virtual int A(){return 0;} virtual int B(){return 0;} }; これが規定クラスだとして、 class CTest : public CBase {public: int A(){return 1;} int B(){return 1;} }; のクラスが存在します。 一番有効だと思ってるのが、上記を変えて CBase :: virtual int A(){throw this_base; return 0;} のように書けば判定できると思いますが、例外を使うよりもいい方法はありませんか。

  • 継承について

    継承についてです。 class A { int x; } class B extends A{ public int test(){ return 1; } public static void main(String[]args){ A a = new B; a.test(); } } としたとき a.test(); という記述はできないものなのでしょうか?a.textでコンパル時にエラーが出ます。aはBのインスタンスを指しているから、test()もつかえるんじゃないかなとおもったのですが・・・ public int test(){return 0;}というのをクラスAに記述すればエラーは起こりませんが、これはわかります。 ウェブを探したのですが、検索の仕方が悪かったのかこれに関する記事をさがせなかったので質問いたしました。

    • ベストアンサー
    • Java
  • オーバーライドされた演算子の継承

    オーバーライドされた演算子は継承されますか? 今やろうとしていることは、変換演算子を継承しようとしています。 試してみたところ警告もなく出来ましたが、この動作は規格上正しいのでしょうか? #include <iostream> class base { public: operator bool() { return true; } }; class derived : public base { }; int main() { derived d; if(d) { std::cout << "true"; } }

  • 継承したクラス側のクラス名の取得

    C#3.5を使用しています。 BaseClassはStackTraceを使ってクラス名を取得できるメソッドを持っています。 それを継承したDerivedClass側でそのクラス名「DerivedClass」を取得したいのですが、 次のようなやり方では「BaseClass」が取得されてしまいます・・・ あくまでBaseClass側に記述したメソッドのままで、DerivedClass側のクラス名を取得するには どのようにしたら良いのでしょうか? public class BaseClass { public string className; public string ClassName { get { if (className == null) { StackTrace st = new StackTrace(false); StackFrame sf = st.GetFrame(0); className = sf.GetMethod().ReflectedType.Name; } return className; } set { className = value; } } } public class DerivedClass : BaseClass { } var c = new DerivedClass(); MessageBox.Show(c.ClassName);// BaseClassと表示される

  • templateクラスについて

    先ほど以下のようなプログラムを書いたのですがコンパイルを通すことができません。 //適当なポインタを保持するだけのクラス template <class _type> class hoge { private:   //適当に変数を保持   _type val; public:   //コンストラクタで適当に値をセット   hoge() : val( 0 ){}   //このクラスから唯一ポインタを引っ張ってくる方法   friend _type getVal( const hoge& foo )   {     // そのまま返す     return foo.val;   } }; void func( const hoge<int>& foo ) {   //値を引き出す   getVal( foo ); } void main() {   //実体化   hoge<int> foo;   //値を引き出す   getVal( foo );   //関数の先で値を引き出す   func( foo ); } 上記のようなプログラムを書いたのですが、main関数内でgetValを呼び出す場合はとくに問題ないのですがfunc関数を呼び出してfunc関数内でgetValを呼び出すと error C3861: 'getVal': 識別子が見つかりませんでした error C2365: 'getVal' : 再定義; 以前の定義は '以前は不明な識別子' でした。 コンパイルされたクラスの テンプレート のインスタンス化 'hoge<_type>' の参照を確認してください というエラーが出てしまいます。 func関数の引数を( const hoge<int>& foo )からvoid func( hoge<int> foo )のように参照渡しから実体渡しに変更するとコンパイルが通り、実行もできるのですが、なぜこれでコンパイルが通るのか理由がいまいちよくわかりません。 またやはり、コンストラクタ、デストラクタの問題などから実体渡しより、参照渡しを使いたいのですがどのようにプログラムを書けば今回の問題を解決できますでしょうか。 よろしくおねがいします。 /* VisualStudio2005 AcademicEdition MicroSoft WindowsXP Professional 32bit */

  • C++基底クラスに戻り値の異なる関数が宣言されている場合

    こんにちは。 質問させてください。 以下のようなコードがあったとします。 class Base1 { public:   virtual int get()=0; }; class Base2 { public:   virtual float get()=0; }; class Ex : public Base1, public Base2 { public:   int get(){return 0;}   float get(){return 0.0f;} }; void main() { } このプログラムをコンパイルすると 「'Ex::get': オーバーライドする仮想関数の戻り値の型が異なり、'Base1::get' の covariant ではありません。」 というエラーが出てしまいます。 関数の名前と引数が同じで戻り値だけが異なる場合はエラーが出るのはわかるのですが、基底クラスのBase1、Base2は変更不可能だとするとどのように回避すればいいのでしょうか? よろしくお願いします。 /*   WindowsXP Professional SP3   VisualStudio2005 AcademicEdition */

  • クラスを配列で確保する場合のコンストラクタへの引数の渡し方

    例えばコンストラクタのオーバーロードで以下のようなクラスを宣言したとします。 class testClass{ public: testClass(void){ num = 10; } testClass(int num1,int num2){ num = num1 + num2; } int num; }; オブジェクトとして宣言する場合 testClass obj(10,10); と定義とすれば、testClass(int num1,int num2)の方が適用されobj.num = 20となり、問題ありませんが、ここでobjを複数定義したい場合、obj[10]と定義すると上手く引数を渡す事ができません。 以下のような形で宣言するという手もありますが testClass obj[2] = {testClass(2,2),testClass(2,2)}; 例えば定数NUMと定義しておき、 testClass obj[NUM]; という形で宣言したい場合、どのようにすればよいでしょうか? よろしくお願いします。

  • private継承はどう使う?

    C++の話です class Base{.....}; class Derived1 : public Base{.....}; class Derived2 : private Base{.....}; と書くことができますが、public継承とprivate継承にはそれぞれ意味がありますよね。 public継承は"is-a"関係を意味していて、private継承は "is-implemented-in-terms-of"関係を表していると言います。 public継承を実際に動くプログラムは思いつくのですが、private継承を使ったプログラムが思いつきません(というより有効に使えません) 派生クラスから呼び出せない、外部からも呼び出せないメンバをどう使うのでしょうか?

  • PHPのクラス継承において質問

    class SuperClass{ public function getSuperClassName (){ //操作中のオブジェクトのクラス名を取得する print get_class($this); print "<br />"; print __CLASS__; } } class SubClass extends SuperClass{ public function getSubClassName (){ //操作中のオブジェクトのクラス名を取得する print get_class($this); print "<br />"; print __CLASS__; } $obj = new SubClass(); print "<br />"; $obj -> getSuperClassName(); print "<br />"; $obj -> getSubClassName(); 上記のようなコードがあった場合、 出力結果は SubClass SuperClass //継承しているにもかかわらず、SuperClassという文字列が返る。 SubClass SubClass と上記のようになります この結果に一点疑問があるのですが、親クラスのメソッドは子クラスに継承されるんですよね? であれば親クラスの public function getSuperClassName (){ //操作中のオブジェクトのクラス名を取得する print get_class($this); print "<br />"; print __CLASS__; } の箇所の print __CLASS__; の記述箇所はSubClassとでなければいけないような気がします。 しかし結果はSuperClassとでますが、このメソッドは子クラスに継承されていないのですか? publicなら継承されるとマニュアルにかいてありましたが、どうなっているのでしょうか よろしくご教授御願い致します。

    • ベストアンサー
    • PHP