65578

Why sizeof(Derived4) is 8 byte? I think it should be 5 bytes

Question:

This is the output of the given program:

sizeof(Empty) 1 sizeof(Derived1) 1 sizeof(Derived2) 4 sizeof(Derived3) 1 sizeof(Derived4) 8 sizeof(Dummy) 1

This is the program:

#include <iostream> using namespace std; class Empty {}; class Derived1 : public Empty {}; class Derived2 : virtual public Empty {}; class Derived3 : public Empty { char c; }; class Derived4 : virtual public Empty { char c; }; class Dummy { char c; }; int main() { cout << "sizeof(Empty) " << sizeof(Empty) << endl; cout << "sizeof(Derived1) " << sizeof(Derived1) << endl; cout << "sizeof(Derived2) " << sizeof(Derived2) << endl; cout << "sizeof(Derived3) " << sizeof(Derived3) << endl; cout << "sizeof(Derived4) " << sizeof(Derived4) << endl; cout << "sizeof(Dummy) " << sizeof(Dummy) << endl; return 0; }

Size of Derived3 is 1 byte. Then why the size of Derived 4 is 8 bytes?? If alignment is the answer then why there is no alignment in case of derived3?

Answer1:

It depends on alignments of data members within a class. It seems that if a class has a virtual base class then its realization contains a reference to this virtual base class that in your case is equal to 4 bytes. When you add data member of type char then it is padded with three bytes that to provide the alignment for the reference to the base virtual class.

Answer2:

For a <strong>concrete type</strong> T, sizeof means two things:

<ul><li>

the representation of a <strong>complete object</strong> a of type T occupies only sizeof(T) bytes in [(char*)&a, (char*)&a + sizeof(T));

</li> <li>

an <strong>array</strong> of T stores the second object sizeof(T) after the first.

</li> </ul>

The bytes occupied by complete objects don't overlap: either one is a subject of the other and is contained in it, or they have no bytes in common.

You can overwrite a complete object (with memset) and then use placement new to reconstruct it (or simply assignment for objects without meaningful construction), and everything will be fine if the destructor wasn't important (don't do that if the destructor is responsible for the release of a resource). You can't overwrite only a base class subobject as it will wreck the complete object. sizeof tells you how many bytes you can overwrite without breaking other objects.

Data members of a class are complete objects, so <strong>the size of a class is always at least the sum of the sizes of its members</strong>.

Some types are "full": every bit in the object is meaningful; notably, unsigned char. Some types have unused bits or bytes. Many classes have such "holes" for padding. An empty class has zero meaningful bit: no bit is part of the state, as there is no state. An empty class is a concrete class, and but instantiated; every instance has an identity hence a distinct address, so its size couldn't be zero even if the standard allowed zero values of sizeof. An empty class is pure padding.

Consider:

struct intchar { int i; char c; };

The alignment of intchar is the alignment of int. On typical system where sizeof(int) is 4 and alignment of these basic types is equal to size, so intchar has alignment 4 and size 8, because the size corresponds to the distance between two array elements, so 3 bytes aren't used for the representation.

Given intchar_char

struct intchar_char { intchar ic; char c; };

the size must be greater than the size of intchar even with unused bytes exist in ic because of alignment: member ic is a complete object and occupies all its bytes, and memset is permitted in this object.

sizeof is well defined only for concrete types (that can be instantiated) and complete objects. So you need sizeof to determine the size of a empty class if you want to create arrays of such; but for base class subobject, sizeof doesn't give you the information you want.

There is no operator in C++ to measure how many bytes are used in the representation of a class, but you can try with a derived class:

template <class Base, int c=1> struct add_chars : Base { char dummy[c]; }; template <class T> struct has_trailing_unused_space { static const bool result = sizeof (add_chars<T>) == sizeof (T); };

Note that add_chars<T> doesn't have a member of type T, so there is no T complete object and memset is not allowed on the intchar subobject. dummy is a complete object that can't overlap with any other complete object but it can overlap with a base class subobject.

<strong>The size of a derived class isn't always at least the sum of the sizes of its subojects.</strong>

The member dummy occupies exactly one byte; if there is any trailing byte in Base, most compilers will allocate dummy in the unused space; has_trailing_unused_space tests this property.

int main() { std::cout << "empty has trailing space: "; std::cout << has_trailing_unused_space<empty>::result; }

<a href="https://ideone.com/lsGfJ9" rel="nofollow">outputs</a>:

<blockquote>

empty has trailing space: 1

</blockquote> <h2>virtual inheritance</h2>

When considering the layout of classes involving virtual functions and virtual bases classes, you need to consider the hidden vptr and internal pointers. They will have the same properties (size and alignment) as a void* in typical implementations.

class Derived2 : virtual public Empty {};

Unlike normal inheritance and membership, virtual inheritance doesn't define a strict, direct, ownership relation, but a shared, indirect ownership, just like calling a virtual function introduces an indirection. Virtual inheritance creates two sorts of class layout: base class subobject and complete object layouts.

When a class is instantiated, the compiler will use the layout defined for complete objects, which can be, using a vptr as GCC does and the Titanium ABI stipulates:

struct Derived2 { void *__vptr; };

The vptr points to a complete vtable, with all the runtime information, but the C++ language doesn't consider such class to be polymorphic class, so dynamic_cast/typeid can't be used to determine the dynamic type.

AFAIK, Visual C++ doesn't use a vptr but a pointer to subobject:

struct Derived2 { Empty *__ptr; };

And other compilers could use a relative offset:

struct Derived2 { offset_t __off; };

Derived2 is very simple class; the subobject layout of Derived2 is the same as its complete object layout.

No consider a slightly more involved case:

struct Base { int i; }; struct DerV : virtual Base { int j; };

Here the complete layout of DerV might be (Titanium ABI-style):

struct complete__DerV { void *__vptr; int j; Base __base; };

The subobject layout is

struct DerV { void *__vptr; int j; };

All complete or incomplete objects of type DerV have this layout.

The vtable contains the relative offsets of the virtual base: offsetof(complete__DerV,__base) in case of an object of dynamic type DerV.

A call to a virtual function can be done by looking up the overrider at runtime or by knowing the dynamic type by language rules.

An upcast (conversion of a pointer to virtual base class), which often happens implicitly when a member function is called on a base class:

struct Base { void f(); }; struct DerV : virtual Base { }; DerV d; d.f(); // involves a derived to base conversion

either uses the known offset when the dynamic type is known, as here, or uses the runtime information to determine the offset:

void foo (DerV &d) { d.f(); // involves a derived to base conversion }

can be translated to (Titanium ABI-style)

void foo (DerV &d) { (Base*)((char*)&d + d.__vptr.off__Base)->f(); }

or Visual C++-style:

void foo (DerV &d) { d.__ptr->f(); }

or even

void foo (DerV &d) { (Base*)((char*)&d + d.__off)->f(); }

The overhead depends on the implementation, but it's there whenever the dynamic type isn't known.

Recommend

  • Dynamic binding in C++ on copied object
  • get only value from JSONArray, i.e. without also the key, in java
  • Windows Phone: RemoveBackEntry after navigate failing
  • Query list of sub documents with RavenDB
  • Move object without a move constructor
  • Why use GC.SuppressFinalize() when there is no Finalizer?
  • Scope(failure) in C++11?
  • I want to trace logs using a Macro multi parameter always null. problem c++ windows
  • Saving an integer in core data
  • Multiple Layouts Previews for Android in Eclipse
  • Form Post with enctype = “multipart/form-data” causing parameters to not get passed
  • Git and client/server code separation
  • CoreData basics – to-many relationship array data
  • C# Excel interop - how to test if interop object is still working and performing a task?
  • In Java, how can I construct a File from a resource?
  • Sensibility of combined Maven/Ant+Ivy build management for dual platform Desktop/Android deployment?
  • Why does java tzupdater add leap seconds?
  • DIV instruction jumping to random location?
  • Diff between two dataframes in pandas
  • JBoss External Properties Files in Classpath
  • Moving Android View and preventing onDraw to be called over and over again
  • xtable package: Skipping some rows in the output
  • Grails calculated field in SQL
  • Launch Runnable Jar from Web Start
  • JavaScriptCore crash on iOS9
  • Is possible to count alias result on mysql
  • Trying to switch camera back to front but getting exception
  • In LanguageTool, how do you create a dictionary and use it for spell checking?
  • Importing jscolor library in angular 2
  • QuartzCore.framework for Mono Develop
  • Warning: Can't call setState (or forceUpdate) on an unmounted component
  • Arrays break string types in Julia
  • How to format a variable of double type
  • VB.net deserialize, JSON Conversion from type 'Dictionary(Of String,Object)' to type '
  • retrieve vertices with no linked edge in arangodb
  • How can I get HTML syntax highlighting in my editor for CakePHP?
  • -fvisibility=hidden not passed by compiler for Debug builds
  • How do I configure my settings file to work with unit tests?
  • IndexOutOfRangeException on multidimensional array despite using GetLength check
  • Binding checkboxes to object values in AngularJs