简体   繁体   English

如果使用vtable实现具有虚函数的类,那么如何实现没有虚函数的类?

[英]If classes with virtual functions are implemented with vtables, how is a class with no virtual functions implemented?

特别是,无论如何都不会有某种功能指针?

I think that the phrase " classes with virtual functions are implemented with vtables " is misleading you. 我认为“ 带有虚函数的类是用vtable实现的 ”这句话会误导你。

The phrase makes it sound like classes with virtual functions are implemented " in way A " and classes without virtual functions are implemented " in way B ". 该短语使得听起来像具有虚拟功能的类是“ 在方式A ”中实现的,而没有虚函数的类是在“方式B ”中实现的。

In reality, classes with virtual functions, in addition to being implemented as classes are, they also have a vtable. 实际上,具有虚函数的类除了作为类实现之外 ,还具有vtable。 Another way to see it is that "'vtables' implement the 'virtual function' part of a class". 另一种看待它的方法是“'vtable'实现类的'虚函数'部分”。

More details on how they both work: 有关它们如何工作的更多细节:

All classes (with virtual or non-virtual methods) are structs. 所有类(使用虚拟或非虚拟方法)都是结构。 The only difference between a struct and a class in C++ is that, by default, members are public in structs and private in classes. C ++中结构和类之间的唯一区别是,默认情况下,成员在结构中是公共的,在类中是私有的。 Because of that, I'll use the term class here to refer to both structs and classes. 因此,我将在这里使用术语class来引用结构和类。 Remember, they are almost synonyms! 记住,它们几乎是同义词!

Data Members 数据成员

Classes are (as are structs) just blocks of contiguous memory where each member is stored in sequence. 类(结构)只是连续内存块,其中每个成员按顺序存储。 Note that some times there will be gaps between members for CPU architectural reasons, so the block can be larger than the sum of its parts. 请注意,由于CPU体系结构原因,某些成员之间会存在间隙,因此块可能大于其各部分的总和。

Methods 方法

Methods or "member functions" are an illusion. 方法或“成员函数”是一种幻觉。 In reality, there is no such thing as a "member function". 实际上,没有“成员函数”这样的东西。 A function is always just a sequence of machine code instructions stored somewhere in memory. 函数始终只是存储在内存中的一系列机器代码指令。 To make a call, the processor jumps to that position of memory and starts executing. 要进行呼叫,处理器会跳转到该内存位置并开始执行。 You could say that all methods and functions are 'global', and any indication of the contrary is a convenient illusion enforced by the compiler. 您可以说所有方法和函数都是“全局的”,任何相反的指示都是编译器强制执行的一种错觉。

Obviously, a method acts like it belongs to a specific object, so clearly there is more going on. 显然,一个方法就像它属于一个特定的对象一样,所以显然会有更多的事情发生。 To tie a particular call of a method (a function) to a specific object, every member method has a hidden argument that is a pointer to the object in question. 为了将方法(函数)的特定调用绑定到特定对象,每个成员方法都有一个隐藏参数,该参数是指向相关对象的指针。 The member is hidden in that you don't add it to your C++ code yourself, but there is nothing magical about it -- it's very real. 该成员是隐藏的,你不会自己将它添加到你的C ++代码中,但没有什么神奇之处 - 它是非常真实的。 When you say this: 当你这样说:

void CMyThingy::DoSomething(int arg);
{
    // do something
}

The compiler really does this: 编译器真的这样做:

void CMyThingy_DoSomething(CMyThingy* this, int arg)
{
    /do something
}

Finally, when you write this: 最后,当你写这个:

myObj.doSomething(aValue);

the compiler says: 编译器说:

CMyThingy_DoSomething(&myObj, aValue);

No need for function pointers anywhere! 无需任何地方的函数指针! The compiler knows already which method you are calling so it calls it directly. 编译器已经知道您正在调用哪个方法,因此它直接调用它。

Static methods are even simpler. 静态方法甚至更简单。 They don't have a this pointer, so they are implemented exactly as you write them. 它们没有this指针,因此它们的编写与编写它们完全相同。

That's is! 那是! The rest is just convenient syntax sugaring: The compiler knows which class a method belongs to, so it makes sure it doesn't let you call the function without specifying which one. 其余的只是方便的语法糖:编译器知道方法属于哪个类,因此它确保它不允许您在不指定哪个类的情况下调用该函数。 It also uses that knowledge to translates myItem to this->myItem when it's unambiguous to do so. 它还使用该知识将myItem转换为this->myItem当它明确无误时。

(yeah, that's right: member access in a method is always done indirectly via a pointer, even if you don't see one) (是的,这是正确的:方法中的成员访问总是通过指针间接完成,即使你没有看到一个)

( Edit : Removed last sentence and posted separately so it can be criticized separately) 编辑 :删除最后一句并单独发布,以便单独批评)

Non virtual member functions are really just a syntactic sugar as they are almost like an ordinary function but with access checking and an implicit object parameter. 非虚拟成员函数实际上只是一个语法糖,因为它们几乎像普通函数,但具有访问检查和隐式对象参数。

struct A 
{
  void foo ();
  void bar () const;
};

is basically the same as: 基本相同:

struct A 
{
};

void foo (A * this);
void bar (A const * this);

The vtable is needed so that we call the right function for our specific object instance. 需要vtable,以便为特定的对象实例调用正确的函数。 For example, if we have: 例如,如果我们有:

struct A 
{
  virtual void foo ();
};

The implementation of 'foo' might approximate to something like: 'foo'的实现可能近似于:

void foo (A * this) {
  void (*realFoo)(A *) = lookupVtable (this->vtable, "foo");
  (realFoo)(this);   // Make the call to the most derived version of 'foo'
}

The virtual methods are required when you want to use polymorphism. 如果要使用多态,则需要虚方法。 The virtual modifier puts the method in the VMT for late binding and then at runtime is decided which method from which class is executed. virtual修饰符将该方法放入VMT以进行后期绑定,然后在运行时决定从哪个方法执行该类。

If the method is not virtual - it is decided at compile time from which class instance will it be executed. 如果该方法不是虚拟的 - 则在编译时决定从哪个类实例执行。

Function pointers are used mostly for callbacks. 函数指针主要用于回调。

If a class with a virtual function is implemented with a vtable, then a class with no virtual function is implemented without a vtable. 如果使用vtable实现具有虚函数的类,则在没有vtable的情况下实现没有虚函数的类。

A vtable contains the function pointers needed to dispatch a call to the appropriate method. vtable包含将调用分派给适当方法所需的函数指针。 If the method isn't virtual, the call goes to the class's known type, and no indirection is needed. 如果该方法不是虚拟的,则调用将转到类的已知类型,并且不需要间接寻址。

For a non-virtual method the compiler can generate a normal function invocation (eg, CALL to a particular address with this pointer passed as a parameter) or even inline it. 对于非虚方法,编译器可以生成正常的函数调用(例如,通过将此指针作为参数传递到特定地址的CALL)或甚至内联它。 For a virtual function, the compiler doesn't usually know at compile time at which address to invoke the code, therefore it generates code that looks up the address in the vtable at runtime and then invokes the method. 对于虚函数,编译器通常不会在编译时知道调用代码的地址,因此它生成的代码在运行时查找vtable中的地址,然后调用该方法。 True, even for virtual functions the compiler can sometimes correctly resolve the right code at compile time (eg, methods on local variables invoked without a pointer/reference). 确实,即使对于虚函数,编译器有时也可以在编译时正确地解析正确的代码(例如,在没有指针/引用的情况下调用局部变量的方法)。

(I pulled this section from my original answer so that it can be criticized separately. It is a lot more concise and to the point of your question, so in a way it's a much better answer) (我从原来的答案中删除了这一部分,以便它可以单独批评。它更简洁,更贴切你的问题,所以在某种程度上它是一个更好的答案)

No, there are no function pointers; 不,没有函数指针; instead, the compiler turns the problem inside-out . 相反,编译器将问题从里到外转变。

The compiler calls a global function with a pointer to the object instead of calling some pointed-to function inside the object 编译器使用指向对象的指针调用全局函数,而不是调用对象内部的某个指向函数

Why? 为什么? Because it's usually a lot more efficient that way. 因为这种方式通常效率更高。 Indirect calls are expensive instructions. 间接呼叫是昂贵的指令。

不需要函数指针,因为它在运行时不能更改。

Branches are generated directly to the compiled code for the methods; 分支直接生成到方法的编译代码; just like if you have functions that aren't in a class at all, branches are generated straight to them. 就像你有完全不在类中的函数一样,分支直接生成它们。

The compiler/linker links directly which methods will be invoked. 编译器/链接器直接链接将调用哪些方法。 No need for a vtable indirection. 不需要vtable间接。 BTW, what does that have to do with "stack vs. heap"? 顺便说一下,这与“堆栈与堆”有什么关系?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 虚函数和vtable是如何实现的? - How are virtual functions and vtable implemented? 在不同类中实现的纯虚函数的继承 - Inheritance of pure virtual functions implemented in different classes 是否需要在派生类中实现所有虚函数? - Do ALL virtual functions need to be implemented in derived classes? 未实现的vtable引用,实现了所有虚函数 - undefined reference to vtable with all virtual functions implemented 使用非虚拟基类函数与派生类未实现虚拟函数之间的区别 - Difference between using non-virtual base class functions versus derived class non-implemented virtual functions C++ 链接时对基类函数的未定义引用(--not-- vtables、构造函数、模板或纯虚函数) - C++ Undefined reference to base class functions at link time (--not-- vtables, constructors, templates, or pure virtual functions) 如何使用虚拟类实现此初始化列表? - How is this initialization list implemented using a virtual class? 错误:尽管实现了所有虚函数,但仍“分配抽象类类型的对象” - Error: "Allocating an object of abstract class type" despite all virtual functions being implemented 我明白为什么了,但是虚拟函数/ VTables如何正确地允许通过指针访问正确的函数呢? - I get why, but how exactly do Virtual Functions/VTables allow the correct functions to be accessed through pointers? C ++父类,其中包含在2个不同子类中实现的虚方法 - C++ Parent class with virtual methods implemented in 2 different child classes
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM