C++ 虚拟函数需要多少字节的额外内存?

C++ 虚拟函数需要多少字节的额外内存?,c++,memory,virtual-functions,C++,Memory,Virtual Functions,虚函数的结构从简单到复杂,有各种各样的情况。什么因素决定了所需的额外内存数量?比如说 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 : pu

虚函数的结构从简单到复杂,有各种各样的情况。什么因素决定了所需的额外内存数量?比如说

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() {}                         }; 
[--------------添加更多有趣的问题----------------]

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

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

  • 我认为虚拟函数的调用速度应该比手动ifs快,因为使用了直接VPTR。对吧?

  • 考试

    我做了一个测试,用VS2010和英特尔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放入对象是为了实现动态分派,因为引用的类可能不是动态类

    (4) 该实现在调用方面效率很高(比手动ifs更快),但会牺牲一些内存


    非常感谢

    由于虚拟函数通常是作为一个对象实现的,所以我猜每个
    都有
    O(虚拟函数的数量)
    个,每个
    对象都有一个额外的指针。没有你想要的那么精确,但应该给出一个大致的想法。
    正如@AlokSave所提到的,还有许多其他细节是非常特定于编译器的,可能不足以进行估计

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

    资料来源:

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

    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);
    }
    
    编译器通常避免在编译时解析调用时使用vtables


    最后,虚拟功能的成本在速度上可能比内存更高

    这将取决于您的编译器和平台。哪些是你感兴趣的?VS2010还是英特尔C++,谢谢。这信息对你来说有什么关系?不应该!如果您需要动态调度,您只需使用
    virtual
    就可以了。这里没有任何开销。您只需为您使用的东西付费。您使用的功能只需支付成本。仅此而已。@PhilipKendall:只是好奇,您知道有多少编译器不使用vtable方法?@AlokSave:这可能很重要。例如,您可以通过不使用vtable并增加对象的内存占用来删除一级间接寻址。毕竟,了解细节是件好事。无知的方法并不是最好的。“因为虚拟函数是作为vtable实现的”,虽然不一定。这完全是实现定义的细节。此外,每个类是否有一个vtable或多个vtable因实现而异。尽管vtable和vpointer机制是实现动态调度的最常见的方法,但所涉及的vtable的数量并不是一个一致的细节。@AlokSave修复了这个问题,我可以发誓,当我写这篇文章时,我脑子里有一个典型的想法:P