【问题标题】:how to determine sizeof class with virtual functions?如何用虚函数确定类的大小?
【发布时间】:2011-01-22 05:23:06
【问题描述】:

这是一个家庭作业问题。对于以下代码,

#include <iostream>
using namespace std;

class A
{
public:
    virtual void f(){}
};

class B
{
public:
    virtual void f2(){}
};

class C: public A, public B
{
public: 
    virtual void f3(){}
};

class D: public C
{
public:
    virtual void f4(){}
};

int main()
{
    cout<<sizeof(D)<<endl;
}

输出为:8

谁能解释一下它是8字节吗?如果 vtable 实现依赖于编译器,我应该在面试中回答这种问题?那么虚拟基类呢?

编辑:我正在使用 32 位平台。

【问题讨论】:

  • sizeof 运算符在大多数情况下对类类型没有真正意义。如果你依赖类的大小来做某事,那么你的设计在某个地方出错的可能性很大(尽管不是 100%)。您能否详细说明您在作业中要完成的工作,或者这是实际的作业问题? (如果是后者并且讲师没有指定特定的编译器或平台,那么唯一正确的答案是“类的大小是实现细节。”)
  • @Jonathan:我很久以前在笔试中遇到过这个问题,在给出的多项选择中没有像“实施依赖”这样的选项。公司名称是菲尔***。我想,我不能在这里提到公司名称:)
  • 那么笔试不允许正确答案。这并不意味着它不正确。 “8 字节”、“两个指针”或“64 位”都不是正确答案,即使它在特定系统/编译器组合上是准确的。
  • 如果有人在面试问题中提出这样的问题,您可能会考虑直接回答:“感谢您的宝贵时间,但我只是决定不想在这里工作。”

标签: c++ sizeof vtable


【解决方案1】:

这当然取决于实现。这会是一个糟糕的面试问题。一个优秀的 C++ 程序员可以相信 sizeof 是正确的,让编译器担心那些 vtable 的事情。

但是这里发生的情况是,典型的基于 vtable 的实现需要在 CD 类的对象中的两个 vtable。每个基类都需要自己的 vtable。 CD新增的虚方法可以通过从一个基类扩展vtable格式来处理,但是AB使用的vtables不能合并。

在伪 C 代码中,这是 D 类型的最派生对象在我的实现中的外观(g++ 4.4.5 Linux x86):

void* D_vtable_part1[] = { (void*) 0, &D_typeinfo, &A::f1, &C::f3, &D::f4 };
void* D_vtable_part2[] = { (void*) -4, &D_typeinfo, &B::f2 };

struct D {
  void** vtable_A;
  void** vtable_B;
};

D d = { D_vtable_part1 + 1, D_vtable_part2 + 1 };

【讨论】:

  • 我不认为这是一个糟糕的面试问题。它将清楚地表明候选人是否知道幕后发生的事情。我同样会询问有关硬件(处理器/缓存/RAM 等)的问题,只是为了了解我愿意招募的人的知识程度。虽然日常任务可能没有必要,但知识永远不会浪费。
【解决方案2】:

在这个问题中,如果你尝试获取 Sizeof 类 A,它会给你答案“4”,因为 A 只有一个虚函数,所以它的 __vptr 将是“4”字节。

同样,如果你尝试获取 Sizeof 类 B,它会给你答案 '4',因为 B 也只有一个虚函数,所以它的 __vptr 将是 '4' 字节。

但是类 C 继承了类 A 和 B 并且 C 本身具有虚函数。所以 C 将接收 2 个 __vptr 指针,并且对于它自己的虚函数 C 将使用继承的 __vptr。 因此,如果您尝试获取 Sizeof 类 C,它会给您答案 '8',因为 C 有两个虚拟指针。

最后,D 类继承了 C 类,因此 D 将继承的 __vptr 用于它自己的虚函数,并且因为 C 类有 sizeof '8' 字节,所以 sizeof D 会给你答案 '8' 字节。

【讨论】:

    【解决方案3】:

    请原谅我含糊其辞,但您提到它本质上是家庭作业。

    查看 sizeof() 为其他类返回什么。您的答案会因您的编译器以及您是在 32 位还是 64 位环境中而有所不同。

    调查愉快!

    【讨论】:

    • 对于 32 位机器,是否有任何标准方法来确定?而不是对每一个案件都进行调查:)
    • 没有标准方法,但是您的 C 类很可能持有两个指向不同虚拟表的指针。
    【解决方案4】:

    对象的大小与它有多少方法无关,也与这些方法是否虚拟无关。对象的大小完全由其成员变量决定。

    我不能确切地告诉你为什么你会得到 8 个字节的大小。由于您的类中没有数据成员,因此 C++ 编译器原则上可以生成一个完全不占用空间的类 [1]!我猜这 8 个字节是提供指向 vtbl 的指针所需的最小值,可能还有一些填充。

    [1] 我认为。目前没有时间检查规范以了解 sizeof 是否可以返回 0。

    【讨论】:

    • @JSBangs:在 32 位平台上工作时,vptr 占用 4 个字节。
    • @JSBangs: sizeof 从不返回零,但基类子对象可以占用零字节。
    • @JSBangs @bjskishore123:D 的大小为 8 字节,因为它包含 两个 vtable 32 位指针。有两个是因为D 类派生自C 类,而C 派生自两个具有虚函数的不同类:A 类和B。几乎在每个 C++ 实现中,虚函数的存在都需要至少一个指向对象中的 vtable 的指针。这就是多态的实现方式。
    • @JSBangs:“对象的大小仅由其成员变量决定。”我很抱歉地说,但这并不完全正确。对象的大小由其成员变量填充虚函数或基的存在决定。
    • @In silico:对,对于C类,它是否在从A,B继承的两个vtable中插入f3?如果没有,C 应该再有一个 vtable,使其大小为 12 字节。
    猜你喜欢
    • 2014-04-02
    • 2020-09-30
    • 2011-10-16
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 1970-01-01
    • 2012-03-18
    相关资源
    最近更新 更多