2

As the size of empty class is 1 byte but when this empty class is virtually inherited that (on gcc compilers) the answer to sizeof(derived1) is coming out to be 8 byte...,how come this is happening as sizeof(derived1) must be sizeof(char)+sizeof(virtual pointer), which is 1+4..i.e 5 byte.,so, from where this extra 3 bytes is coming...???

#include <iostream>
using namespace std;

class Empty
{};

class Derived1 : virtual public Empty<br>
{
    char c;
};

int main()
{
    cout << "sizeof(Empty) " << sizeof(Empty) << endl;
    cout << "sizeof(Derived1) " << sizeof(Derived1) << endl;
    return 0;
}
seenukarthi
  • 8,241
  • 10
  • 47
  • 68
Anuraag
  • 33
  • 4
  • Are you compiling with or without optimization (`-O1` or up)? – Marc Claesen Jul 24 '14 at 09:25
  • Chances are that everything is aligned to 4 byte boundaries on your machine. – Joseph Mansfield Jul 24 '14 at 09:26
  • 2
    "as X must be Y"... don't go around making assertions. – Kerrek SB Jul 24 '14 at 09:27
  • 2
    @MarcClaesen Optimization doesn't have anything to do with it. (Otherwise, you couldn't link optimized modules with unoptimized ones.) – James Kanze Jul 24 '14 at 09:29
  • @JamesKanze Thanks, I didn't know. I thought object size was determined in the linking process. I assumed that compiling without optimization could, for example, add extra debugging info to classes. Another lesson learnt! – Marc Claesen Jul 24 '14 at 09:35
  • @MarcClaesen There is an issue concerning extra debugging information; that is usually controlled by preprocessor defines (`_ITERATOR_DEBUG_LEVEL` for VS, `_GLIBCXX_DEBUG` and `_GLIBCXX_DEBUG_PEDANTIC` for g++), and you will get runtime errors if you try to link object files compiled when these are different (at least if the sources in question use the standard library). – James Kanze Jul 24 '14 at 10:11

2 Answers2

5

It's all unspecified as far as the C++ standard is concerned, but typically virtual inheritance adds a pointer or two, which in turn imposes additional alignment constraints; the size of a class with virtual inheritance or virtual functions will almost certainly have to be a multiple of the size of a pointer (4 or 8 on most systems).

James Kanze
  • 150,581
  • 18
  • 184
  • 329
2

The problem is in structure memory alignment. For more details check this atricle: http://en.wikipedia.org/wiki/Data_structure_alignment As you can see the memory padding depends on your platform.

Explanation:

Let's try to debug memory used for your class. As we know variables are not virtuals. That means there is no record of vintrual table to determine which variable to use. So compiler does not need to create virtual functions table unless you have no virtual functions. On my machine virtual functions table pointer member is called __vfptr. On debug you will not be able to find the member with this name unless you add an virtual function. That means you should remove virtual functions pointer size from you calculations. Try to run following code and read the comments for details:

struct Empty
{

};

struct VirtualNotEmpty: public virtual Empty
{
    char c;
};

struct VirtualNotEmptyEx: public virtual Empty
{
    char c1;
    char c2;
    char c3;
    char c4;
};

struct TrueVirtualNotEmpty: public virtual Empty
{
    virtual void f() {}
    char c;
};

void inspect_vne()
{
    VirtualNotEmpty vne;

    char* start_of_struct = (char*)(&vne);
    char* end_of_struct = (char*)(&vne);
    end_of_struct += sizeof(vne);
    char* start_of_var = (char*)(&(vne.c));

    printf("sizeof(VirtualNotEmpty): %d\n", sizeof(vne));                    // 8 bytes
    printf("Address of struct %p\n", start_of_struct);
    printf("Lenght of struct %d\n", (int)(end_of_struct - start_of_struct));    // same as sizeof
    printf("Address of var c %p\n", start_of_var);
    printf("Diff from start: %d\n", (int)(start_of_var - start_of_struct));        // 4 bytes: 1 byte for struct and 3 bytes for padding
    printf("Diff from end: %d\n", (int)(end_of_struct - start_of_var));            // 4 bytes:    1 byte for struct and 3 bytes for padding

    // [st][pd][pd][pd] [ch][pd][pd][pd]
    // st -- struct vt -- virtual table, pd -- padding byte ch -- char member variable
}

void inspect_tvne()
{
    TrueVirtualNotEmpty vne;

    char* start_of_struct = (char*)(&vne);
    char* end_of_struct = (char*)(&vne);
    end_of_struct += sizeof(vne);
    char* start_of_var = (char*)(&(vne.c));

    printf("sizeof(TrueVirtualNotEmpty): %d\n", sizeof(vne));                //    12
    printf("Address of struct %p\n", start_of_struct);
    printf("Lenght of struct %d\n", (int)(end_of_struct - start_of_struct)); // same as sizeof
    printf("Address of var c %p\n", start_of_var);
    printf("Diff from start: %d\n", (int)(start_of_var - start_of_struct)); // 8 bytes: 1 byte for struct + 3 bytes of struct padding + 4 bytes o vptr
    printf("Diff from end: %d\n", (int)(end_of_struct - start_of_var));        // 4 bytes: 1 byte for member variable + 3 bytes for padding

    // [st][pd][pd][pd] [vt][vt][vt][vt] [ch][pd][pd][pd]
    // st -- struct vt -- virtual table, pd -- padding byte ch -- char member variable
    // member variable TrueVirtualNotEmpty::c can't be packed at first byte, because of order of dectaled members __vptr is declared bevore
    // TrueVirtualNotEmpty::c
}

int main(int, char**)
{
    printf("Size of pointer %d", sizeof(int*)); // 4 on my machine    
    inspect_vne();
    inspect_tvne();
    printf("Size of VirtualNotEmptyEx %d\n", sizeof(VirtualNotEmptyEx));    // there is no need to create padding at
                                                                            // end for c1-c4 chars because there is space to pack them
    return 0;
}

As you can see size of struct on my machine should be dividable by 4.

Edward
  • 304
  • 2
  • 16
  • 1
    You should flesh this answer more as it's not much better than a link answer – EdChum Jul 24 '14 at 10:00
  • Can you explain about `1 byte for the structure`? I 've been thought before, that size of structure equals the sum of memory occupied with all it's variables (including padding) and in the case of `VirtualNotEmpty` it has to be 4. So where does extra byte come from? – DotNetter Jul 25 '14 at 11:46
  • @Necrolis already answered to your question: http://stackoverflow.com/a/7920924/968765 – Edward Jul 25 '14 at 12:05