8

我想我理解虚拟方法和 vtables 的概念,但我不明白为什么将对象作为指针(或引用)传递和按值传递(哪种废弃 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
4

7 回答 7

19

因为如果按值传递,那么就会发生对象切片,无法实现运行时多态。在您的代码中,这一行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 '&'

只有在最后一行你没有实现运行时多态性。

于 2011-04-28T15:31:35.453 回答
4

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

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

于 2011-04-28T15:31:31.027 回答
3

虚函数在您的两个示例中都运行良好。它们完全按照应有的方式工作。

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

在您的第一个示例中,您创建了一个类型为 的对象Texture。对象的动态类型是Texture,所以虚调用去的方法Texture

在第二种情况下,您创建一个类型为 的对象Material。对象的动态类型是Material,所以虚调用去的方法Material

这里的所有都是它的。一切都按预期进行。如果您的期望与此不同,那么您应该使它们与语言更好地保持一致。

于 2011-04-28T15:33:30.097 回答
2

因为Material m = Texture();切片对象 - 在这一点上,你只有一个Material.

于 2011-04-28T15:31:47.997 回答
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
于 2011-04-28T15:31:08.800 回答
1
Material m = Texture();

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

于 2011-04-28T15:31:54.207 回答
1
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& not的成员函数Texture

于 2011-04-28T15:38:48.587 回答