2012-01-09 15 views
3

のベクタータイプの損失私はクラスBおよびC そしてYに親クラスX、親であるクラスAを有し、Z.のC++における多型、親クラス

class A {}; 
class B : public A {}; 
class C : public A {}; 


class X 
{ 
    void foo(A) { std:: cout << "A"; } 
}; 

class Y : public X 
{ 
    void foo(B) {std::cout << "B"; } 
}; 

class Z : public X 
{ 
    void foo(c) {std<<cout <<"C"; } 
}; 

int main() 
{ 
    B b; 
    C c; 

    Y y; 
    Z z; 

    y.foo(b);//prints B // b is a B, and Y::foo takes a B, hence print B 
    y.foo(c);//prints A // mismatch between types, fall back and print A 
    z.foo(b);//prints A // mismatch between types, fall back and print A 
    z.foo(c);//prints C // c is a C, and Y::foo takes a C, hence print C 

    std::vector<A> v; 
    v.push_back(b); 
    v.push_back(c); 

    //In this loop, it always prints A, but *this is what I want to change* 
    for (size_t i = 0; i < v.size(); ++i) 
    { 
     z.foo(v.at(i)); 
     y.foo(v.at(i)); 
    } 
} 

ハードコーディングされたコールと同じ結果を印刷するアイテムを取得することはできますか? 私は彼らの親のタイプではなく元のタイプとして扱うことを意味しますか? または一度私はそれらをAのベクトルにint型に入れます。永遠にA型になりますか?

+0

'y.foo(C)、(virtual destructorは、とにかくここに良いアイデアをでしょう)。 //プリントA '。確かにこれは望ましい行動ではありませんか?私はあなたが "B"を印刷したいと思う?私はあなたが望む行動を明確にする必要があると思います。 'p.foo(q)'を指定すると、印刷されたテキストは、qのタイプ、pのタイプ、またはその両方に依存しますか? '{x、y、z} .foo({a、b、c});}という9つのオプションをすべて考えて、あなたの望む動作が何であるかを教えてください。 –

+0

希望の動作が最初のプリント 意味y.foo(b)はBを出力し、y.foo(c)は基本クラスx.foo()を呼び出してA – Bg1987

+0

OKを出力する必要があります。したがって 'p.foo(q)'では:qの(動的)タイプが 'p :: foo'のパラメータのタイプと同じであれば、そのタイプを出力する必要があります。それ以外の場合は、 "A"が印刷されます。 –

答えて

6

あなたが見ているものはObject Slicingです。
派生クラスのオブジェクトを、Baseクラスのオブジェクトを格納するはずのベクトルに格納しています。これはオブジェクトのスライスにつながり、格納されているオブジェクトの派生クラス固有のメンバはスライスオフになります。 Baseクラスのオブジェクトとして機能します。

ソリューション:

あなたはベクトルで基本クラスのオブジェクトへのポインタ保存する必要があります。

vector<X*> 

そこにはスライスないだろうと希望を達成することができ基底クラスへのポインタを格納することにより多形的な振る舞いも関数を作ることによって実現する。virtual
適切なアプローチは、ベクトルに生ポインタを格納する代わりに、適切なSmart pointerを使用することです。それはあなたが手動でメモリを管理する必要がないことを保証します、RAII自動的にそれを行います。

+0

ベクトルをに変更することを意味しますか? – Bg1987

+0

私は関数をvirtualとして@vasile状態にする必要もあると信じていますが、これも間違いなくスライスしています。 – greg

+0

@greg:OPは既に関数が 'virtual'であると述べていますが、Opはポインタではなくオブジェクトを格納します。 –

1

スライシングと呼ばれます。 push_backの要素をstd::vector<A>に入れると、基本的にはAの新しく構築されたインスタンスにelementsがコピーされます。したがって、派生クラスから来たオブジェクトの一部は失われます(「スライスされた」)。あなたの代わりに要素のポインタを格納した容器を使用する必要がスライスしないようにするために

ので、あなたが使用する必要がありますstd::vector<A*>またはあなたの要素は、ヒープ好ましくsmartpointer(std::shared_ptrまたはstd::unique_ptrのいくつかの並べ替えでのベクトルを割り当てられている場合そうでなければC++ 11、boost::shared_ptrまたはstd::tr1::shared_ptr)。ので、それらがもたらすであろう逆参照、あなたのベクトル内のすべての要素がタイプA*を持つことになりながら XYZすべては、値によってそのパラメータを取る:書かれたとして、あなたのコードは、あなたがいることを変更しても、動作しません。しかし

A、それでも間違ったメソッドが呼び出されます。これは、常にA&A*を取るために署名を変更し、型にそれをキャストしようとするdynamic_castを使用することによって解決することができます。もちろんdynamic_cast

class X 
{ 
    void foo(A*) { std:: cout << "A"; } 
}; 

class Y : public X 
{ 
    void foo(A* p) { 
     if (dynamic_cast<B*>(p)) std::cout << "B"; // requires virtual methods in A 
     else     X::foo(p); 
    } 
}; 

class Z : public X 
{ 
    void foo(A*){ 
     if (dynamic_cast<C*>(p)) std::cout << "C"; // requires virtual methods in A 
     else     X::foo(p); 
    } 
}; 

は少し高価であるが、それが問題だ場合は、再考する場合がありますあなたのデザイン。そうでない場合dynamic_castが動作しませんので、あなたがA, B, Cは、いくつかのvirtualメソッドが含まれていることを確認する必要があります)

+0

私はあなたがそれを釘付けにしていると思いますが、パラメータの動的タイプとX、Y、Zが望むタイプが一致する場合にのみ、 "B"または "C" *を出力します。私はOPからのコメントに基づいて少し質問を編集しました。 –

関連する問題