为什么虚函数需要用指针传递而不是(对象的)值传递?

时间:2011-04-28 15:28:16

标签: c++ inheritance polymorphism virtual-functions object-slicing

我认为我理解虚方法和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

7 个答案:

答案 0 :(得分:17)

因为如果你传递值,那么object slicing将会发生,并且无法实现运行时多态性。在您的代码中,行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 '&'

仅在最后一行中您没有实现运行时多态性。

答案 1 :(得分:4)

Material m = Texture()会调用构造函数Material::Material(Texture const &),或者,如果不可用,则调用Material复制构造函数,构造Material而不是Texture

这无法为您构建Texture对象,因此该对象切片到基类对象。

答案 2 :(得分:3)

虚拟函数在两个示例中都能很好地工作。他们的工作方式完全符合预期。

虚函数的整体思想是根据调用中使用的对象的 dynamic 类型调度对此类函数的调用。 (很遗憾,您没有在示例中显示如何进行这些调用。)

在第一个示例中,您创建了一个Texture类型的对象。对象的动态类型为Texture,因此虚拟调用将转到Texture的方法。

在第二种情况下,您将创建一个Material类型的对象。对象的动态类型为Material,因此虚拟调用将转到Material的方法。

这就是它的全部。一切都按照人们的预期运作。如果你的期望与此不同,那么你应该让它们更好地与语言保持一致。

答案 3 :(得分:2)

因为Material m = Texture();对对象进行切片 - 此时,您只有一个Material

答案 4 :(得分:1)

将“纹理”对象指定给“材质”后,会将其切割为“材质”。因此,对m对象的任何调用都只会调度Material函数。

Material m只有一个Material对象的空间。

使用普通结构,我说明了切片的含义:

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

答案 5 :(得分:1)

Material m = Texture();

这会创建一个临时Texture,然后通过复制Material的{​​{1}}部分来创建Material。这称为切片,通常不是您想要的。

答案 6 :(得分:1)

class Base
{
    //Members       
};

class Derived1:public Base
{
    //Members
};

int main()
{
    Base obj1;
    Derived1 obj2;

    obj1 = obj2;   //Allowed Since Public Inheritance 
}

obj1 = obj2仅将从Base Class继承的Derived Class obj2的成员复制到obj1时,Derived Class的其余成员将被切掉。这只是因为Base类obj1不知道Derived类的成员。这个现象被称为Object Slicing

在您的情况下,当您致电Material m = Texture()时,只包含Material的成员,因此对象上的任何函数调用都会调用Material&的成员函数。不是Texture