C++虚函数表、虚继承,类的大小

本文详细解释了C++中虚函数表的大小如何由虚函数数量决定,以及父类和子类虚函数表的区别。讨论了虚函数对对象大小的影响,涉及虚继承、虚函数继承和构造/析构函数中虚函数调用的性质。
摘要由CSDN通过智能技术生成

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++ 虚调用及其调用的具体形式
虚调用是相对于实调用而言,它的本质是动态联编。
在发生函数调用的时候,如果函数的入口地址是在编译阶段静态确定的,就是是实调用。
反之,如果函数的入口地址要在运行时通过查询虚函数表的方式获得,就是虚调用。

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中的多态性是通过虚函数实现的。在含有虚函数中,编译器会自动添加一个指向虚函数指针,这个指针通常称为虚函数指针虚函数是一个存储虚函数地址的数组,每个有一个对应的虚函数。当一个对象被创建时,会自动分配一个指向它的虚函数指针虚函数指针大小虚函数大小都与具体实现相关。在一般情况下,虚函数指针大小为4或者8个字节虚函数大小取决于虚函数的个数。 以下是一个模拟实现: ```c++ #include <iostream> using namespace std; class A { public: virtual void func1() { cout << "A::func1" << endl; } virtual void func2() { cout << "A::func2" << endl; } }; class B : public A { public: virtual void func1() { cout << "B::func1" << endl; } }; int main() { A* a = new A(); B* b = new B(); cout << "size of A: " << sizeof(A) << endl; cout << "size of B: " << sizeof(B) << endl; cout << "size of a: " << sizeof(a) << endl; cout << "size of b: " << sizeof(b) << endl; a->func1(); a->func2(); b->func1(); b->func2(); delete a; delete b; return 0; } ``` 输出结果: ``` size of A: 8 size of B: 8 size of a: 8 size of b: 8 A::func1 A::func2 B::func1 A::func2 ``` 在上面的代码中,我们定义了两个A和B,其中B继承自A。A和B都含有虚函数,因此编译器会为它们添加虚函数指针。在main函数中,我们创建了一个A对象和一个B对象,并输出了它们的大小以及指针大小。接着我们调用了每个对象的虚函数,可以看到B对象的func1()覆盖了A对象的func1(),而A对象的func2()没有被覆盖。最后我们删除了这两个对象,避免内存泄漏。 需要注意的是,虚函数指针大小虚函数大小是不确定的,取决于具体实现。此外,虚函数指针通常被放在对象的开头,因此虚函数通常被放在内存中较靠前的位置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值