views:

330

answers:

2

For the following code fragment.

/*This program demonstartes how a virtual table pointer 
 * adds to a size of a class*/

class A{

};

class X{
    public:
        void doNothing(){}
    private:
        char a;

};

class Z:public X {

    public:
        void doNothing(){}
    private:
        char z;

}; 

class Y{
    public:
        virtual void doNothing(){}
    private:
        char a;

};

class P:public Y {

    public:
        void doNothing(){}
    private:
        char pp[4];

};

int main(){
    A a;
    X x;
    Y y;
    Z z;
    P p;
    std::cout << "Size of A:" << sizeof(a) << std::endl;// Prints out 1
    std::cout << "Size of X:" << sizeof(x) << std::endl;//Prints out 1
    std::cout << "Size of Y:" << sizeof(y) << std::endl;//Prints 8
    std::cout << "Size of Z:" << sizeof(z) << std::endl;
//Prints 8 or 12 depending upon wether 4 bytes worth of storrage is used by Z data member.
    std::cout << "Size of P:" << sizeof(p) << std::endl;
    std::cout << "Size of int:" << sizeof(int) << std::endl;
    std::cout << "Size of int*:" << sizeof(int*) << std::endl;
    std::cout << "Size of long*:" << sizeof(long*) << std::endl;
    std::cout << "Size of long:" << sizeof(long) << std::endl;
    return 0;

}

The behaviour I seem to notice is that whenever an empty class is instantiated or an empty class is inherited from byte boundaries are not considered(ie: an object of size 1 byte is allowed), in every other case object size seems to be determined by byte boundaries.

Whats the reason for this? I ask since at this point I am guessing.

+5  A: 

I'm not sure what the question is about, but I'll make a wild guess and assume that you're confused by the fact that sizeof(A)==1, X derives from A adding a char field, and yet sizeof(X)==1 (while you'd expect it to be 2 - one for A, one for char in X).

This is known as "empty base class optimization". In C/C++, an object must have a non-zero size (ISO C++ 1.8[intro.object]/5) - this indirectly implies that every object has a distinct address, unions notwithstanding - so even for an empty class, it still has to be at least 1 byte. However, when an object is a base class subobject of another object, this restriction is lifted. Thus, an instance of A must be at least 1 byte by itself, but when A is a base class of another class, there's no requirement for that anymore, and the compiler can get rid of that dummy padding entirely; so the size of X comes only from its char field.

Pavel Minaev
Sorry to nit pick, but C does not have objects. So it should be "In C++ and object must have a non-zero size..."
Dima
C does have objects. In both C and C++, the term "object" does not have anything to do with OOP, however. From ISO C99, section 3 "Terms, definitions and symbols", subsection 3.14: "object - region of data storage in the execution environment, the contents of which can represent values". ISO C++ uses a similar definition. An instance of a class in C++ is "object of class type" (while an `int` value is an "object of non-class type").
Pavel Minaev
Sorry about the horribly convoluted question.
Pradyot
If in stroustrup's example (pointed by Dima), you replace the "int" by the "Empty" type (so the first member, *and* the base-class is of type "Empty"), then there will be padding to ensure the first member and the empty base class will not have the same address. So only objects having the same type have to have distinct adddresses. Objects of different types can share addresses, i believe. (I read that in the vandervoorde template book - but i could have misunderstood it)
Johannes Schaub - litb
@Dima: to expland on Pavel's comment about 'objects' in C, the term is used to distingush between such things as an enumeration (which is not an object) and a variable with an enum type (the variable is an object) or a pointer to struct (which is a pointer to an object) vs. a function pointer (which is not a pointer to an object).
Michael Burr
@litb: `struct X : Empty { Empty a; };` still has `sizeof() == 1` and member `a` at the address of the object's start (at least in my quick test). I see no reason why member `a` would need to have a different address.
Michael Burr
@Michael Burr: what compiler/version? With gcc 4.3, struct X : empty { empty a; } has size 2, while struct Y : empty { char ch; } has size 1. That being said, the standard says [class]/3: "Complete objects and member subobjects of class type shall have non-zero size - Base class subobjects are not so constrained" then again, in [class.derived]/5: "A base class subobject may be of zero size; however, two subobjects that have the same class type and belong to the same most derived object must not be allocated at the same address".
David Rodríguez - dribeas
I find no restriction in the standard that objects of different type have to have different addresses (even complete objects). If they don't store data (obviously, then they need different addresses) it seems to me they can be at the same address. So it seems to me the following assert could theoretically pass: `struct EmptyA { } a; struct EmptyB { } b; assert ((void*)`.
Johannes Schaub - litb
@litb: as dribeas points out, Standard requires all objects to have non-zero size. Since objects can only overlap in unions, this also means that all objects have to have unique addresses - the _only_ exception to this is base class subobjects, where what you say holds true.
Pavel Minaev
@Pavel, i can't find where it states that objects may not overlap. In the itanium ABI, though, i read that the Standard requires compilers not to overlay tail padding for POD structs. Do you know where the Standard says overlapping is not valid for objects unless they are subobjects?
Johannes Schaub - litb
Note that array objects also overlap their elements :)
Johannes Schaub - litb
You're right, and structs also overlap their fields. I think that 1.8/2..3 covers all cases: "Objects can contain other objects, called sub-objects. A sub-object can be a member sub-object (9.2), abase class sub-object (clause 10), or an array element. An object that is not a sub-object of any other objectis called a complete object."
Pavel Minaev
"For every object x, there is some object called the complete object of x, determined as follows:— If x is a complete object, then x is the complete object of x.— Otherwise, the complete object of x is the complete object of the (unique) object that contains x."
Pavel Minaev
I think i will ask on c.l.c++.m about the object overlapping. I'll post you the link.
Johannes Schaub - litb
+3  A: 

Here is Stroustrup's explanation of why size of an empty class cannot be zero. As to why it is 1 byte, as opposed to something that conforms to the alignment boundaries, I would guess this depends on the compiler.

Dima