0

I am trying to fully understand how class sizes are determined in C++ so I can keep this in mind when designing data structures. I have some classes that I have created for testing but seem to struggle on some of the sizes, perhaps due to not knowing the order in which the sizes are added together.

Consider the following on a 64-bit compilation

class A {
public:
    virtual void work() {}

private:
    float a;
    float b;
};

class B : A {
private:
    float b1;
};

class C : A {
private:
    float c1;
};

class D : C {
private: 
    float d1;
};

struct DCA {
    float d1;
    float c1;
    float a;
    float b;
    void* function;
};

int main()
{
    std::cout << sizeof(A) << std::endl;//16
    std::cout << sizeof(B) << std::endl;//24
    std::cout << sizeof(C) << std::endl;//24
    std::cout << sizeof(D) << std::endl;//32
    std::cout << sizeof(DCA) << std::endl;//24;
}

Now I understand that a pointer is created when using a virtual function which on 64-bit adds 8 bytes.

  1. Is this pointer only for the class that has the virtual function, or the derived class also.

  2. What is the order in which I would calculate the size. If I was calculating the size of D, would I start by calculating the size of A, then C, then D?

  3. struct DCA has the same variables as class D, however its size is 24 and the size of class D is 32. I would have expected D to be 24 also as I'm counting the floats first which is 16 bytes, and then 8 bytes for the pointer. 24 is divisible by 8 which is 8 byte aligned.

Can someone attempt to answer these questions and tell me where I am going wrong with my logic?

enter image description here


The duplicate question does not address virtual keyword, inheritance or order of calculations. It also doesn't mention whether or not there is a standard specific byte alignment.

jjmcc
  • 795
  • 2
  • 11
  • 27
  • 2
    Possible duplicate of [Why isn't sizeof for a struct equal to the sum of sizeof of each member?](https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member) – Algirdas Preidžius Sep 21 '18 at 14:26
  • FWIW, you are in implementation defined land. The standard really doesn't have anything to say about class size except for standard layout classes. It's going to hard to give you a definitive answer as it depends on your implementation and code. In fact if the compiler can prove it knows the run time type you wont even have a virtual call because it is allowed to optimize it away. – NathanOliver Sep 21 '18 at 14:27
  • @NathanOliver Let's assume that it's not optimized away and it's just the code I have posted. I just want to understand how it's calculated i.e. is there a specific order things are done (which may cause padding). Like if A was calculated before B, then B gets added, there would be additional padding for the 8-byte alignment. – jjmcc Sep 21 '18 at 14:35
  • Related, see [Structure padding and packing](https://stackoverflow.com/q/4306186/608639), [Struct memory layout in C](https://stackoverflow.com/q/2748995/608639), [Optimizing member variable order in C++](https://stackoverflow.com/q/892767/608639), etc. – jww Sep 21 '18 at 16:56

1 Answers1

0

Is this pointer only for the class that has the virtual function, or the derived class also.

Since the derived class contains the base class as its sub-object, the derived class also has that pointer to vtable.

What is the order in which I would calculate the size. If I was calculating the size of D, would I start by calculating the size of A, then C, then D?

From base to derived. For multiple base classes, left-to-right. This is because there is no padding in front of the first member.

struct DCA has the same variables as class D

They are of the same size, namely 24 bytes.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • How does `D` have 5 floats? I'm only seeing `d1`, `c1`, `a` and `b`? – jjmcc Sep 21 '18 at 14:43
  • @jjmcc my mistake. – Maxim Egorushkin Sep 21 '18 at 14:43
  • In which case, why do `DCA` and `D` differ in size? I assume it's because of padding when calculating `C`? Also, once the pointer is added, does that mean that everything else that follows must be 8-byte aligned? – jjmcc Sep 21 '18 at 14:44
  • They do not seem to be the same size though... If I run the code posted in the OP I get `32` for `D` and `24` for `DCA`. If I run in 32-bit then I get `20` for both, but in 64-bit they differ. – jjmcc Sep 21 '18 at 14:46
  • Not for me. I'm using Visual Studio 2017. Will different compilers produce different results? – jjmcc Sep 21 '18 at 14:49
  • @MaximEgorushkin I have added an image to my OP which displays the output in order. I just want to know why it is different. – jjmcc Sep 21 '18 at 14:55
  • @jjmcc Visual Studio compilers have not been known for standard conformance. – Maxim Egorushkin Sep 21 '18 at 14:55
  • This still doesn't explain what exactly is happening. Does this mean that the VS compiler cannot be trusted and that another should be used? – jjmcc Sep 21 '18 at 15:04
  • @MaximEgorushkin I deleted my comment. I read `void * function()` instead of `void * function` – agbinfo Sep 21 '18 at 15:31
  • @jjmcc There must be an explanation for Visual Studio behaviour. Do you get the same output for debug and release builds? (I do not have VS to try) – Maxim Egorushkin Sep 21 '18 at 15:33
  • I may be missing something... Seems to me they /should/ have different sizes, sizeof(A)=16 because it contains an 8-byte pointer and two 4-byte floats. It should have alignof(A)=8. Then C has another float, so it could be 20-bytes, but it always must be aligned to 8-byte boundaries so it gets padded out so that sizeof(C)=24. For the same reason, sizeof(D)=32 with 8 total bytes of padding. DCA needs no padding, so sizeof(DCA)=24. Is there something wrong in my thinking? – Jeff Garrett Sep 21 '18 at 16:07
  • Ah, I think I understand. The compiler is permitted to reuse trailing padding of the base class subobject for a derived object, but isn't required to. – Jeff Garrett Sep 21 '18 at 16:14
  • @JeffGarrett It can reuse padding when using private inheritance, but not when public. – Maxim Egorushkin Sep 21 '18 at 16:21
  • Hmm. Any chance you have a reference for that? – Jeff Garrett Sep 21 '18 at 16:52