私はそれをどのように理解しているかを説明しようと思います。私を助けるヒントは、レゴのピースについて考えることです。
あなたの場合、2つのレゴピースがあります。1つは名前が付けられA
、もう1つは名前が付けられています...しかし、ピースが2つのピースを取り付けて形成されたピースであるB
と想像してください。一方のピースは、同じタイプです。B
A
A B
+-+ +-+-+
|a| |a|b|
+-+ +-+-+
次に、ポインターを使用して各レゴピースを参照しますが、各ピースには独自の形状があるため、想像してみてください。
A* pa =new A();
B* pb =new B();
A* paUpcast= new B();
A *pa --> +-+ new A()
|a|
+-+
B* pb --> +-+-+ new B()
|a|b|
+-+-+
A* paUpcast --> +-+-+ new B()
|a|b|
+-+-+
paUpcast
ポインタは型のポインタですが、型の一部A
を保持していることに注意してください。ご覧のとおり、その部分はベースよりもわずかに大きい部分です。B
B
A
これはあなたが話しているアップキャストです。ベースポインタは、継承ツリーで下向きに関連するものをすべて保持できるワイルドカードのようなものです。
A * paUpcast = new B();
上記の行は次の行と同等ですか?
A * paUpcast =(A *)B;
さて、あなたが本当にこれを書きたいと仮定すると:A* paUpcast = (A*) new B();
はい、そうです。B
クラスの新しいインスタンスを作成し、それをA
クラスへのポインターに格納します。ポインターに割り当てる前に新しいインスタンスを変換しても、基本クラスのポインターに格納されるという事実は変わりません。
次の2つのコードを使用できない理由。
B * pbDowncast = new A();
B * pbDowncast =(B *)A;
レゴのピースを覚えておいてください。やっている間に何が起こりB* pbDowncast=new A()
ますか?:
B* pbDowncast --> +-+ new A()
|a|
+-+
新しいベースクラスインスタンスを作成し、それを派生クラスへのポインタに格納します。レゴピースが収まらない場合は、ベースを派生クラスとして処理しようとしています。作品は、その種と見なされるために必要な余分なものA
を欠いています。これらすべてのものは、レゴピースの余分な部分に「保存」されます。B
B = all the A stuff plus something more
B
+-+-----------------------------------+
|a|extra stuff that only B pieces have|
+-+-----------------------------------+
B
クラスだけが持っているメソッドを呼び出そうとするとどうなりますか?すべてのメソッドをB
呼び出すことができるポインターがB
ありますが、作成したインスタンスA
は、メソッドを持たない種類のものでありB
、これらすべての余分なものを使用して作成されたわけではありません。
ただし、入力すると
B * pbDowncast =(B *)pa;
pbDowncast-> f();
「B」の代わりに「A」を表示すると、矛盾が発生します。
それは私にとって矛盾に似ていません、レゴの部分を思い出して、pa
ポインターはタイプの部分を指していますA
:
A *pa --> +-+
|a|
+-+
この作品にはすべてのB
ものが欠けています。実際には、標準の出力にf()
印刷する方法がありません...しかし、出力に印刷する方法があります。B
f()
A
お役に立てば幸いです。
編集:
ダウンキャストを使うのも不適切だということにも同意しているようですね。
いいえ、同意しません。ダウンキャスティングはまったく不適切ではありませんが、用途によっては不適切になります。すべてのC++ツールと同様に、ダウンキャスティングにはユーティリティと使用範囲があります。良い使い方を尊重するすべてのトリックは適切です。
では、ダウンキャスティングツールの良い使い方は何でしょうか?私見では、コードやプログラムフローを壊さず、コードを可能な限り読みやすく維持し、プログラマーが自分のしていることを知っていれば(私にとって最も重要です)。
継承の可能性のあるブランチを取得するダウンキャストは、結局のところ一般的な方法です。
A* paUpcast = new B();
static_cast<B*>(paUpcast)->f();
しかし、より複雑な継承ツリーでは面倒です。
#include<iostream>
using namespace std;
class A{
public:
virtual void f()
{
cout<<"A"<<endl;
}
};
class B: public A{
public:
virtual void f()
{
cout<<"B"<<endl;
}
};
class C: public A{
public:
virtual void f()
{
cout<<"C"<<endl;
}
};
A* paUpcast = new C();
static_cast<B*>(paUpcast)->f(); // <--- OMG! C isn't B!
これに対処するために、あなたは利用することができますdynamic_cast
A* paUpcast = new C();
if (B* b = dynamic_cast<B*>(paUpcast))
{
b->f();
}
if (C* c = dynamic_cast<C*>(paUpcast))
{
c->f();
}
しかし、パフォーマンスの欠如でdynamic_cast
よく知られています。内部オブジェクト識別子や変換演算子など、いくつかの代替案を検討できますが、質問に固執するために、ダウンキャストは正しく使用されていればまったく悪くありません。dynamic_cast