C++虚函数表所占的大小?
在C++中,虚函数表指针的大小和虚函数表的大小都与具体实现相关。在一般情况下,虚函数表指针的大小为4(32位)或者8(64位)个字节,虚函数表的大小取决于类中虚函数的个数。
以下是一个C++虚函数表大小的具体例子:
#include <iostream>
using namespace std;
class Base {
public:
virtual void func1() { cout << "Base::func1()" << endl; }
virtual void func2() { cout << "Base::func2()" << endl; }
};
class Derived : public Base {
public:
virtual void func3() { cout << "Derived::func3()" << endl; }
};
int main() {
Derived d;
d.func1(); // calls Base::func1()
d.func2(); // calls Base::func2()
d.func3(); // calls Derived::func3()
return 0;
}
在这个例子中,Derived
类继承了Base
类,并且重写了其中两个虚函数。由于Derived
类中有三个虚函数,因此它也有自己的虚函数表,大小为三个指针(指向虚函数的地址)。在运行时,当调用d.func1()
和d.func2()
时,程序会从Derived
类的虚函数表中查找这两个函数的地址,并调用它们。当调用d.func3()
时,程序会从Derived
类的虚函数表中查找这个函数的地址,并调用它。
在C++中,虚函数表的大小通常由编译器自动处理,因此我们不需要手动计算它。需要注意的是,虚函数表的大小并不是唯一的,因为不同的编译器和操作系统可能会有不同的实现方式。例如,一些编译器可能会使用更紧凑的数据结构来存储虚函数表,从而减少内存占用。此外,一些操作系统可能会对虚函数表进行优化,以提高程序的性能。
父类与子类的虚函数表是同一张还是不同的表?
在C++中,父类和子类的虚函数表是不同的表。每个类都有自己的虚函数表,其中包含了该类中定义的所有虚函数的地址。当一个类派生自另一个类时,它的虚函数表会继承自基类的虚函数表,但是它不会共享同一张表。
这种设计的好处是可以避免多态性破坏继承关系。如果父类和子类共享同一张虚函数表,那么当一个对象从父类类型转换为子类类型时,可能会出现一些问题。例如,如果一个虚函数被调用时,它在子类中的实现与在父类中的实现不同,那么这个调用可能会产生未定义的行为。通过将每个类的虚函数表分开存储,可以避免这种问题的发生。
子类的虚函数表创建参考一篇文章带你学懂C++虚函数表的继承问题
当父类定义了虚函数时,在子类进行继承的时候会将父类的虚函数表也给继承下来。所以那一些虚函数在子类中也是virtual类型的,如果要对父类中的虚函数进行重写时或添加虚函数,顺序是:
①先将父类的虚函数列表复制过来
②重写虚函数时是把从父类继承过来的虚函数表中对应的虚函数进行相应的替换。
③如果子类自己要添加自己的虚函数,则是把添加的虚函数加到从父类继承过来虚函数表的尾部。
有虚函数的对象比没有虚函数的对象所占字节大小有区别吗?
- 有了虚函数以后,对象所占用的存储空间比没有虚函数时多了 4 (32位系统为4,64位系统为8)个字节。
- 实际上,任何有虚函数的类及其派生类的对象都包含这多出来的 4(32位系统为4,64位系统为8) 个字节,这就是实现多态的关键——它位于对象存储空间的最前端,其中存放的是虚函数表的地址。
虚继承、虚函数继承中具体类大小的计算
参见C++中虚继承类的sizeof大小
空类占据1个字节。
子类虚继承父类时,会有虚基类指针,每多一个父类就多一个虚基类指针,32位机器中每个指针为4 bytes,64位机器中每个指针为8 bytes。
子类、父类的虚函数表不共通,关于表的大小,子类会在复制了父类的虚函数表基础上,加上自己新建的虚函数个数*4(32位系统为4,64位系统为8);而计算有虚函数表的类的大小时,因为类内只存储一个指向虚函数表的指针,因此就是占一个指针大小,32位机器中每个指针为4 bytes,64位机器中每个指针为8 bytes。
构造函数里调用虚函数是实引用还是虚引用?
实调用
由于从概念上说,在一个对象的构造函数运行完毕之前,这个对象还没有完全诞生,所以在构造函数中调用虚函数,实际上都是实调用。析构也是如此,对虚函数的调用是实调用。
实调用与虚调用参考C++ 虚调用及其调用的具体形式
虚调用是相对于实调用而言,它的本质是动态联编。
在发生函数调用的时候,如果函数的入口地址是在编译阶段静态确定的,就是是实调用。
反之,如果函数的入口地址要在运行时通过查询虚函数表的方式获得,就是虚调用。