(オブジェクトの)値ではなく、ポインターで仮想関数を渡す必要があるのはなぜですか?
-
26-10-2019 - |
質問
私は仮想的な方法とvtableの概念を理解していると思いますが、なぜオブジェクトをポインター(または参照)として渡すことと価値(vtableまたは何かのスクラップの種類)を渡すことに違いがある理由を理解していません。
なぜこのようなものが機能するのか:
Material* m = new Texture;
poly->setMaterial(m);
// methods from Texture are called if I keep carrying the pointer around
これではありませんか?:
Material m = Texture();
poly->setMaterial(m);
// methods from Material are called if I pass the value around
解決
あなたが価値を通過するなら、それから オブジェクトスライシング ランタイムの多型を達成することはできません。そして、あなたのコードでは、まさにその行です Material m = Texture()
オブジェクトのスライシングを引き起こします。たとえ合格しても m
ポインター(または参照)では、ランタイムの多型を達成できません。
また、ランタイムの多型は以下を通じて達成されます。
- ベースタイプのポインター、または
- ベースタイプの参照
したがって、ランタイムの多型が必要な場合は、どちらも使用しています ポインター また 参照 ベースタイプの、そしてここにあなたがランタイムの多型を達成する方法をいくつかあります:
Material* m1 = new Texture();
poly->setMaterial(m1); //achieved
Texture* t1= new Texture();
poly->setMaterial(t1); //achieved
Texture t2;
poly->setMaterial( &t2); //achieved : notice '&'
Material & m2 = t2;
poly->setMaterial( &m2 ); //achieved : notice '&'
Material m3;
poly->setMaterial( &m3 ); //NOT achieved : notice '&'
最後の行でのみ、ランタイムの多型を達成できません。
他のヒント
Material m = Texture()
コンストラクターを呼び出します Material::Material(Texture const &)
, 、または、それが利用できない場合、 Material
コンストラクターをコピーします Material
ではなく Texture
.
これが構築できる方法はありません Texture
あなたのためのオブジェクトので、オブジェクトはです スライスされた 基本クラスのオブジェクトへ。
仮想関数は、両方の例で完全にうまく機能します。彼らは働くことになっているとまったく同じように働きます。
仮想関数の全体的な考え方は、そのような関数への呼び出しがに従って派遣されるということです 動的 通話で使用されるオブジェクトのタイプ。 (残念ながら、これらの呼び出しをどのように行うかを例に示しませんでした。)
最初の例では、タイプのオブジェクトを作成しました Texture
. 。オブジェクトの動的なタイプはです Texture
, 、したがって、仮想呼び出しは Texture
.
2番目のケースでは、タイプのオブジェクトを作成します Material
. 。オブジェクトの動的なタイプはです Material
, 、したがって、仮想呼び出しは Material
.
それだけです。すべてが期待されるように機能します。あなたの期待がこれと異なる場合、あなたはそれらを言語とよりよく整合させるだけです。
なぜなら Material m = Texture();
オブジェクトをスライスします - この時点で、あなたはただ持っています Material
.
テクスチャオブジェクトを素材に割り当てると、素材にスライスされます。したがって、Mオブジェクトの呼び出しは、マテリアル関数のみを発送します。
Material m
ちょうど1つのマテリアルオブジェクトのスペースがあります。
プレーン構造体を使用して、スライスすることの意味を示します。
struct A {
int a;
};
struct B : public A {
int b;
};
A objectA = B();
objectA.b = 1; // compile error, objectA does only have the properties of struct A
Material m = Texture();
これにより、一時的なものが作成されます Texture
, 、次にaを作成します Material
コピーすることによって Material
一部の Texture
. 。これはスライシングと呼ばれ、通常はあなたが望むものではありません。
class Base
{
//Members
};
class Derived1:public Base
{
//Members
};
int main()
{
Base obj1;
Derived1 obj2;
obj1 = obj2; //Allowed Since Public Inheritance
}
いつ obj1 = obj2
基本クラスから継承された派生クラスOBJ2のメンバーのみがOBJ1にコピーされ、派生クラスのメンバーの残りがスライスされます。これは、基本クラスOBJ1が派生クラスのメンバーを認識していないためです。この現象は呼ばれます Object Slicing
.
あなたの場合、あなたが電話するとき Material m = Texture()
のメンバーのみが含まれています Material
したがって、オブジェクトの関数呼び出しは、メンバー関数を呼び出します Material
& いいえ Texture
.