2

虚函数的结构从简单到复杂都有各种各样的案例。什么因素决定了它需要多少额外的内存?例如

class A            {virtual void F() {}   };
class B : public A {virtual void F() {}   };

与没有虚函数的类相比,A 和 B 需要多少内存?另一个有 2 个虚函数的例子,

class A            {virtual void F() {}   virtual void G() {}   };
class B : public A {virtual void F() {}   virtual void G() {}   };

和示例 3

class A            {virtual void F() {};   virtual void G() {}  };
class B : public A {virtual void F() {}                         }; 

[---------------添加更多有趣的问题------]

  1. 每个虚拟方法都有一个虚拟方法表吗?如果是这样,我认为对于具有多个虚拟方法的类(不是对象,对吗?我认为 VPTR 是静态的。)需要多个 VPTR,每个用于一个虚拟方法。对?

  2. 是否可以为所有虚拟方法只构建一个表并在一个类中只放置一个 VPTR?

  3. 而且我觉得虚函数的调用速度应该比手动ifs要快,因为使用的是直接的VPTR。对?

[ - - - - - - - - 一个测试 - - - - - - - - ]

我做了一个测试,并用 VS2010 和 intel c++ 编译器展示了我的结果。

struct A
{
    static int s_i;
    int i;
    virtual void F()    {i+=1;}
    virtual void G()    {i+=2;}
};

struct B
    : public A
{
    int j;
    virtual void F()    {i+=3;}
    virtual void G()    {i+=4;}

    virtual void H()    {i+=5;}
};

struct C
    : public B
{
    virtual void F()    {i+=6;}

    virtual void H()    {i+=7;}
};

TEST(MemoryForVirtualMethod)
{
    CHECK_EQUAL(sizeof(A), 8);
    CHECK_EQUAL(sizeof(B), 12);
    CHECK_EQUAL(sizeof(C), 12);
}

从结果来看,我的结论是

(1) 对于每个具有虚函数的对象,添加一个(隐藏指针)VPTR。

(2)对每个类,为该类的所有虚方法建立一个虚方法表。

(3) 将VPTR放在object中是为了实现动态调度,因为引用的类可能不是动态类。

(4)实现调用效率高(比手动ifs快)但牺牲了一些内存。

非常感谢!

4

1 回答 1

3

由于虚函数通常实现为虚方法表 (vtable),我猜测O(number of virtual functions)perclass和 1 个额外的指针object。不像你想要的那样精确,但应该给出一个粗略的想法。正如@AlokSave 所提到的,还有许多其他的细微之处是编译器特定的,并且可能不够一致,无法估计。

通常,编译器为每个类创建一个单独的 vtable。创建对象时,会添加一个指向此 vtable 的指针,称为虚拟表指针 vpointer 或 VPTR,作为此对象的隐藏成员

来源:维基百科

您覆盖的函数的数量(如您的示例中所示)是(我猜的)无关紧要的,除非可以在编译时安全地确定要调用的函数,在这种情况下,它可能被视为静态调度函数(G()在您的第二例子)。这也取决于编译器。

只要可以在编译时解析调用,编译器通常会避免使用 vtables。

最后,虚函数的成本可能比内存更难。

于 2013-01-23T06:39:08.123 回答