[英]I get why, but how exactly do Virtual Functions/VTables allow the correct functions to be accessed through pointers?
I found some excellent threads on this site for the subject, and the topic of polymorphism is cleared up for me, but I'm just confused how exactly a virtual function works versus a normal function. 我在该站点上找到了该主题的出色线索,而多态性的主题也为我清除了,但是我只是对虚函数与普通函数的工作原理感到困惑。
(an example given in this thread Why do we need virtual functions in C++? ): (此线程中给出的示例为何我们需要C ++中的虚函数? ):
class Animal
{
public:
void eat() { std::cout << "I'm eating generic food."<<endl; }
};
class Cat : public Animal
{
public:
void eat() { std::cout << "I'm eating a rat."<<endl; }
};
void func(Animal *xyz) { xyz->eat(); }
So We have a function and a derived function that was redefined. 因此,我们有一个函数和一个重新定义的派生函数。
Cat *cat = new Cat;
Animal *animal = new Animal;
animal->eat(); // Outputs: "I'm eating generic food."
cat->eat(); // Outputs: "I'm eating a rat."
func(animal); // Outputs: "I'm eating generic food."
func(cat); // Outputs: "I'm eating generic food."
So we can't access the function we want without it being a virtual function. 因此,如果它不是虚拟函数,就无法访问所需的函数。 But why, exactly? 但是为什么呢?
If the following works: 如果以下有效:
Cat cat;
Animal animal;
animal.eat(); // Outputs: "I'm eating generic food."
cat.eat(); // Outputs: "I'm eating a rat."
Then presumably there are two different eat functions in memory already without needing a vtable. 然后大概在内存中已经有两个不同的eat函数,而无需使用vtable。
So when we make eat a virtual function, each class now gets its own vTable with its own functions. 因此,当我们制作一个虚拟函数时,每个类现在都将获得具有自己函数的vTable。 So...We are just storing the functions in another place in memory. 所以...我们只是将函数存储在内存中的另一个位置。 So what happens to a pointer between when it calls a regular function through an object and when it calls a virtual function through an object? 那么当指针通过对象调用常规函数与通过对象调用虚拟函数之间发生了什么变化?
As in what's the difference between: Animal->eat(); 就像之间的区别是:Animal-> eat(); //Calling a virtual function and Animal->eat(); //调用虚拟函数和Animal-> eat(); //Calling a regular function //调用常规函数
When we declare virtual function, TutorialsPoint says 当我们声明虚函数时,TutorialsPoint说
This time, the compiler looks at the contents of the pointer instead of it's type 这次,编译器查看指针的内容,而不是类型
Yes, but how? 是的,但是怎么? Why couldn't it do that before? 为什么以前不能做到这一点? It's presumably just stored in memory the same as a regular function. 大概它只是和常规函数一样存储在内存中。 Does it have something to do with the Vtable pointer at the beginning of an object? 它与对象开头的Vtable指针有关吗?
I'm just looking for specifics to understand why. 我只是在寻找具体细节以了解原因。 I don't mean to sound like I'm getting bogged down in something ultimately pointless. 我并不是说听起来好像陷入了最终毫无意义的事情。 Just wondering for academic reasons. 只是出于学术原因想知道。
Consider this code: 考虑以下代码:
void Function(Animal *foo)
{
foo->eat();
}
If eat
is a non-virtual member function, this just calls Animal::eat
. 如果eat
是非虚拟成员函数,则仅调用Animal::eat
。 It makes no difference what foo
points to. foo
指向什么没有关系。
If eat
is a virtual member function, this is roughly equal to *(foo->eatPtr)();
如果eat
是一个虚拟成员函数,则大致等于*(foo->eatPtr)();
. 。 You can think of Animal
, and all its derived classes, as having a member variable that points to the eat
function. 您可以将Animal
及其所有派生类视为具有指向eat
函数的成员变量。 So if foo
actually points to a Bear
, then foo->eatPtr()
will access Bear::eatPtr
and call the eat
function of the Bear
class. 因此,如果foo
实际上指向Bear
,则foo->eatPtr()
将访问Bear::eatPtr
并调用Bear
类的eat
函数。
Which function to call is determined at compile time for non-virtual functions. 对于非虚拟函数,在编译时确定要调用哪个函数。 So this will always call the same eat
function. 因此,这将始终调用相同的eat
函数。 For a virtual function, the pointer passed in is used to find the appropriate virtual function table for the particular class that foo
happens to be a member of. 对于虚函数,传入的指针用于为foo
恰好是其成员的特定类找到适当的虚函数表。
This extra class member variable that points to the vtable for the class is why the size of either a class instance or its pointers (depending on the implementation) will typically go up by the size of one pointer when you add the first virtual
function to that class. 指向该类的vtable的这个额外的类成员变量是为什么当您向其添加第一个virtual
函数时,类实例或其指针(取决于实现)的大小通常会增加一个指针的大小的原因类。
I find it best to implement it to understand it. 我发现最好实施它来理解它。
struct ifoo;
struct ifoo_vtable{
void(*print)(ifoo const*);
};
struct ifoo{
ifoo_vtable const* vtable;
void print()const{ vtable->print(this); }
};
struct fooa:ifoo{
void print_impl()const{ std::cout<<"fooa\n"; }
fooa(){
static const ifoo_vtable mytable={
+[](ifoo const* self){
static_cast<fooa const*>(self)->print_impl();
}
};
vtable=&mytable;
}
};
struct foob:ifoo{
void print_impl()const{ std::cout<<"foob\n"; }
foob(){
static const ifoo_vtable mytable={
+[](ifoo const* self){
static_cast<foob const*>(self)->print_impl();
}
};
vtable=&mytable;
}
};
now there is zero use of virtual
above. 现在virtual
上面的使用为零。 But: 但:
fooa a;
foob b;
ifoo* ptr = (rand()%2)?&a;&b;
ptr->print();
will randomly call either fooa or foob's print_impl method. 将随机调用fooa或foob的print_impl方法。
The vtable is a structure of pointers to functions. vtable是指向函数的指针的结构。 Invoking a virtual methid actuall runs a little stub that looks up the method in the vtable, then runs it. 调用虚拟方法实际运行一个小桩,该桩在vtable中查找方法,然后运行它。
Code written for you in implementatiin classes' constructors fills that vtable with function pointers pointing at the overrides. 在实现类的构造函数中为您编写的代码用指向覆盖的函数指针填充该vtable。
Now there are details that are not done here -- calling conventions, destructors, devirtualization, multiple interface inheritance, dynamic casts, etc -- but the core is pretty similar to how every major C++ compiler implements virtual methods. 现在,这里有未完成的细节(调用约定,析构函数,去虚拟化,多接口继承,动态强制转换等),但是其核心与每个主要C ++编译器如何实现虚拟方法非常相似。
Under the standard this is not detailed; 根据标准,这没有详细说明。 only behaviour is. 唯一的行为就是。 But this kind of vtable comes from before C++ was a language, and this kind of vtable is what I believe the C++ language designers had in mind when they specified the behaviour of virtual functions in C++. 但是这种vtable起源于C ++是一种语言之前,而我认为C ++语言设计人员在指定C ++中的虚函数行为时要牢记这种vtable。
Note that this is far from the only way to do this. 请注意,这远非唯一的方法。 MFC message maps, objective C/small talk based messages, python, lua, and many other languages have other ways to do this with advantages and disadvantages over C++'s solution. MFC消息映射,基于目标C /基于聊天的消息,python,lua和许多其他语言具有其他实现此目的的方法,它们比C ++解决方案具有优势和劣势。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.