I think I understand the concept of virtual methods and vtables, but I don't understand why there is a difference between passing the object as a pointer(or reference) and passing it by value (which kind of scraps the vtable or something?)
Why would something like this work:
Material* m = new Texture;
poly->setMaterial(m);
// methods from Texture are called if I keep carrying the pointer around
And not this?:
Material m = Texture();
poly->setMaterial(m);
// methods from Material are called if I pass the value around
Because if you pass by value, then object slicing will occur, and runtime polymorphism cannot be achieved. And in your code, the very line Material m = Texture()
causes object slicing. So even if you pass m
by pointer (or reference), runtime polymorphism cannot be achieved.
Also, runtime polymorphism is achieved through:
- pointer of base type, or
- reference of base type
So if you want runtime polymorphism, you've use either pointer or reference of base type, and here are few examples how you can achieve runtime polymorphism:
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 '&'
Only in the last line you don't achieve runtime polymorphism.
Material m = Texture()
would call the constructor Material::Material(Texture const &)
, or, if that's not available, the Material
copy constructor, which construct a Material
rather than a Texture
.
There is no way this can build a Texture
object for you, so the object is sliced to a base class object.
Virtual functions works perfectly well in both of your examples. They work exactly as they are supposed to work.
The whole idea of a virtual function is that an call to such function is dispatched in accordance with the dynamic type of the object used in the call. (Unfortunately, you didn't show in your examples how you make these calls.)
In your first example, you created an object of type Texture
. The dynamic type of the object is Texture
, so the virtual calls go to the methods of Texture
.
In the second case you create an object of type Material
. The dynamic type of the object is Material
, so the virtual calls go to the methods of Material
.
That's all there is to it. Everything works just as one would expect. If your expectations are different from this, then you should just bring them in better alignment with the language.
Because Material m = Texture();
slices the object - at this point, you just have a Material
.
Once you assign a Texture objecto to a Material, it is sliced to a Material. Therefore, any call on the m Object will dispatch only the Material functions.
Material m
has space for exactly one Material object.
Using plain structs, I illustrate what is meant by slicing:
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();
This create a temporary Texture
, then creates a Material
by copying the Material
part of Texture
. This is called slicing, and is typically not what you want.
class Base
{
//Members
};
class Derived1:public Base
{
//Members
};
int main()
{
Base obj1;
Derived1 obj2;
obj1 = obj2; //Allowed Since Public Inheritance
}
When obj1 = obj2
only those members of the Derived Class obj2 that are inherited from Base Class get copied in to obj1, rest of the members of Derived Class get sliced off. This is simply because Base class obj1 is not aware of members of Derived class. This phenomemon is called Object Slicing
.
In your case, when you call Material m = Texture()
only contains members of Material
and hence any function call on the object calls member functions of Material
& not Texture
.
来源:https://stackoverflow.com/questions/5821075/why-do-virtual-functions-need-to-be-passed-with-a-pointer-and-not-by-valueof-th