C++ virtual function table memory cost

前端 未结 13 1727
抹茶落季
抹茶落季 2020-12-03 08:14

Consider:

class A
{
    public:
        virtual void update() = 0;
}

class B : public A
{
    public:
        void update() { /* stuff goes in here... */ }
         


        
相关标签:
13条回答
  • 2020-12-03 08:39

    The v-table is per class and not per object. Each object contains just a pointer to its v-table. So the overhead per instance is sizeof(pointer) (usually 4 or 8 bytes). It doesn't matter how many virtual functions you have for the sizeof the class object. Considering this, I think you shouldn't worry too much about it.

    0 讨论(0)
  • 2020-12-03 08:41

    If you really want to save memory of virtual table pointer in each object then you can implement code in C-style...

    E.g.

    struct Point2D {
    int x,y;
    };
    
    struct Point3D {
    int x,y,z;
    };
    
    void Draw2D(void *pThis)
    {
      Point2D *p = (Point2D *) pThis;
      //do something 
    }
    
    void Draw3D(void *pThis)
    {
      Point3D *p = (Point3D *) pThis;
     //do something 
    }
    
    int main()
    {
    
        typedef void (*pDrawFunct[2])(void *);
    
         pDrawFunct p;
         Point2D pt2D;
         Point3D pt3D;   
    
         p[0] = &Draw2D;
         p[1] = &Draw3D;    
    
         p[0](&pt2D); //it will call Draw2D function
         p[1](&pt3D); //it will call Draw3D function
         return 0; 
    }
    
    0 讨论(0)
  • 2020-12-03 08:44

    If you know all of the derived types and their respective update functions in advance, you could store the derived type in A, and implement manual dispatch for the update method.

    However, as others are pointing out, you are really not paying that much for the vtable, and the tradeoff is code complexity (and depending on alignment, you might not be saving any memory at all!). Also, if any of your data members have a destructor, then you also have to worry about manually dispatching the destructor.

    If you still want to go this route, it might look like this:

    class A;
    void dispatch_update(A &);
    
    class A
    {
    public:
        A(char derived_type)
          : m_derived_type(derived_type)
        {}
        void update()
        {
            dispatch_update(*this);
        }
        friend void dispatch_update(A &);
    private:
        char m_derived_type;
    };
    
    class B : public A
    {
    public:
        B()
          : A('B')
        {}
        void update() { /* stuff goes in here... */ }
    
    private:
        double a, b, c;
    };
    
    void dispatch_update(A &a)
    {
        switch (a.m_derived_type)
        {
        case 'B':
            static_cast<B &> (a).update();
            break;
        // ...
        }
    }
    
    0 讨论(0)
  • 2020-12-03 08:45

    Given all the answers that are already here, I think I must be crazy, but this seems right to me so I'm posting it anyways. When I first saw your code example, I thought you were slicing the instances of B and C, but then I looked a little closer. I'm now reasonably sure your example won't compile at all, but I don't have a compiler on this box to test.

    A * array = new A[1000];
    array[0] = new B();
    array[1] = new C();
    

    To me, this looks like the first line allocates an array of 1000 A. The subsequent two lines operate on the first and second elements of that array, respectively, which are instances of A, not pointers to A. Thus you cannot assign a pointer to A to those elements (and new B() returns such a pointer). The types are not the same, thus it should fail at compile time (unless A has an assignment operator that takes an A*, in which case it will do whatever you told it to do).

    So, am I entirely off base? I look forward to finding out what I missed.

    0 讨论(0)
  • 2020-12-03 08:51

    How many instances of A-derived classes do you expect? How many distinct A-derived classes do you expect?

    Note that even with a million of instances, we are talking about a total of 32MB. Up to 10 millions, don't sweat it.

    Generally you need an extra pointer per instance, (if you are running on an 32 bit platform, the last 4 byte are due to alignment). Each class consumes additional (Number of virtual functions * sizeof(virtual function pointer) + fixed size) bytes for its VMT.

    Note that, considering alignment for the doubles, even a single byte as type identifier will bring up the array element size to 32. So Stjepan Rajko's solution is helpful in some cases, but not in yours.

    Also, don't forget the overhead of a general heap for so many small objects. You may have another 8 bytes per object. With a custom heap manager - such as an object/size specific pool allocator - you can save more here and employ a standard solution.

    0 讨论(0)
  • 2020-12-03 08:52

    Not an answer to the question directly, but also consider that the declaration order of your data members can increase or decrease your real memory consumption per class object. This is because most compilers can't (read: don't) optimize the order in which class members are laid out in memory to decrease internal fragmentation due to alignment woes.

    0 讨论(0)
提交回复
热议问题