C++ Class Layout
C++ Class Layout and Other Information

C++ Class Layout

Class Layout:

Only non-static data members will contribute to the size of the class object. If we have static and non-virtual members are not do anything with respect to the size of the class. Let's consider the following example. 

 class Base {

  };

 

 class Data {

    public:

        static int data_object_counter;

         void print_info() {

             cout<<"\n Checking the class size";

          }

 }

 

 Base baseObj;

 Data dataObj;

 cout<<"\n Size of Base Object :"<<sizeof(baseObj);

 cout<<"\n Size of Data Object:"<<sizeof(dataObj);        

If we print the size of each class object provides the same. Let's see if we have the same class members inside the class and try to find the size of the class object.

 class Reader {

    public:

      enum { max_object = 100}; /* define 300 as max object count */

       static size_t num_of_object;

       Reader *readerObjArray[max_object]; // declaring the 300 same objects

       size_t size;

  };        

size of the object class is 808 (depending on the compiler) and if we comment on the size inside the class, then 8 will get reduced. Reader class is basically an empty class, it means the size should be zero. We are creating 100 Reader objects inside the class of pointer type. Hence each pointer holds the 8-byte address space.

Static Data Members, Type Members, and Non-virtual member functions have no effect on class size.

Implementation of Single Inheritance:

If the class has a virtual function, every object of that type contains a pointer to the shared virtual function table. 

No alt text provided for this image

Other_stuff:code: Let's see what's all there in other_stuff.

  • The Vtable is a dumping ground for most type-related information about a class.
  • The "other_stuff" is usually
  • An offset to the start of the complete object
  • A pointer to the typeinfo for the object
  • Offsets to virtual base class subobjects in the complete object
  • Additionally, we know that a proper polymorphic base class has a public virtual destructor.
  • A single destructor will be often represented by two separate entries in the virtual function table; one is a "destroy and delete" destructor. While other only destroys.

The virtual calling sequence is indirect through the object's virtual function table. The call represents as follows.

basePtr->getIntegerNumber();        

Translation Looks like: (*(basePtr->vPtr)[0])(basePtr) 

r in other words:

MOV R0,R4 #get basePtr into "this" register

LDR R1, [R4, #+0] #get bp->vptr

LDR R1, [R1, #+0] # get vptr[0] (member function address)

MOV LR, RC #set return link

BX R1 # calling member function

Implementation of overriding:

  • The derived class has its own virtual table.
  • Overriding derived class functions replace those of the base class

No alt text provided for this image
/*
 * checking the single inheritance
 */
#include <iostream>
using namespace std;


class Base
{
public:
    Base() : _integerNumber(0) {}
    virtual int getIntegerNumber();
    virtual void setIntegerNumber(int);


private:
    int _integerNumber;
};


int Base::getIntegerNumber()
{
    return _integerNumber;
}


void Base::setIntegerNumber(int input)
{
    this->_integerNumber = input;
}


class Derived : public Base
{
private:
    int _IntNumber;


public:
    int getIntegerNumber() { return _IntNumber; } // overrides Base::getIntegerNumber()
    void displayInteger() { cout << "\n Integer : " << _IntNumber; }
};


class Shape
{
private:
    int _x_axis;
    int _y_axis;


public:
    Shape() : _x_axis(0), _y_axis(0) { cout << "\nShape::Shape()"; }
    Shape(int x) : _x_axis(x), _y_axis(x) { cout << "\nShape::Shape(int)"; }
    Shape(int x, int y) : _x_axis(x), _y_axis(y) { cout << "\n Shape::Shape(int,int)"; }
    int get_x_axis() { return _x_axis; }
    int get_y_axis() { return _y_axis; }
};


class Subject
{
private:
    string _name;


public:
    Subject() : _name("") { cout << "\nSubject::Subject()"; }
    Subject(string name) : _name(name) { cout << "\nSubject::Subject(string)"; }
    string getShapeName() { return _name; }
};


class ObservedShape : public Shape, public Subject
{
private:
    int _calculateArea;


public:
    ObservedShape() : Shape(100, 100), Subject("Reactangle") { cout << "\n Observed::Observed()"; }
    int getArea() { return (this->get_x_axis() * this->get_y_axis()); }
};

int main()
{
    Base *basePtr = new Base();
    basePtr->setIntegerNumber(100);


    basePtr = new Derived();
    basePtr->setIntegerNumber(800);                       // invoke base call function
    cout << "\n Derived:" << basePtr->getIntegerNumber(); // invoke derived class function


    // mutliple Inheritance Object
    ObservedShape *obs = new ObservedShape();
    Shape *shape = obs;
    Subject *subj = obs;


    cout << "\n X axis : " << obs->get_x_axis();
    cout << "\n Y axis : " << obs->get_y_axis();
    cout << "\n Area : " << obs->getArea();
    cout << "\n Name : " << obs->getShapeName();


    // Let compare the object Shape and ObservedShape object
    if (obs == shape)
    {
        cout << "\n Compiler comparision Successful" << endl;
    }


    // static cast comparision
    ObservedShape *obsPtr = new ObservedShape();
    Shape *shapePtr = obsPtr; // no delta
    Subject *subPtr = obsPtr; // delta


    obsPtr = static_cast<ObservedShape *>(shapePtr); // no delta
    obsPtr = static_cast<ObservedShape *>(subPtr);   // delta


    obsPtr = (ObservedShape *)shape;
    obsPtr = (ObservedShape *)subPtr;
    return 0;
}        

Implementation of Multiple Inheritance:

  • If there is more than one base class it's usually the case that not all base classes can occupy the same location within a derived class object.
  • The derived class object will have more than a single base class sub-object

 Object with Multiple Addresses:

  • Suppose we refer to the same ObserveShape object through each its types
  • Some objects occur at a non-zero delta from the start of the complete object.
  • Both addresses are valid addresses of the derived class object

Note: An object can have more than one valid address.

No alt text provided for this image

Static cast under multiple inheritances:

  • The same address adjustment takes place for static cast

Note: The dynamic_cast operator usually uses a different mechanism and more expensive.

Under multiple inheritances an object may have more than one vptr:

 Problem: The existence of multiple addresses introduces a problem: If a pointer to B2 is used to call an overriding D member function, the value of the "this" pointer will be incorrect.

 B2 *b2p = new D();

B2p->f2();

Let see check the traditional implementation, one of the early implementation store delta adjustments for the "this" pointer in the virtual table itself.

 This approach has the unfortunate property of having to perform the delta arithmetic even when it is not needed.

No alt text provided for this image
No alt text provided for this image

 Superannuated calling sequences: This approach imposed a space and time overhead on all virtual calls even if multiple inheritances are not used.

  • Every virtual table entry store delta, even if it is zero
  • Every virtual call adds a delta to this, even if it is zero

 Example:

Base2 *base2Ptr = get_me_a_Derived();

Base2ptr->print3();

 Was translated: (*base2ptr->vptr[1].funcaddr)(base2ptr+vptr[1].delta)

If the object is passed through void* intermediary, that required type information is going to be lost and not able to access any of the pointer information.  

Duplicate Subobjects:

If the same base class appears more than once in the class, we will get copies of base object info in complete class object info.

No alt text provided for this image

With the virtual base class, we can avoid the above problem. Inherit the classes as virtual and solve the duplicate sub-objects of the class.

Virtual Base classes problems :

  • Virtual Base classes are complex in structure.
  • They introduce the problem with initialization and default initialization of class member variables.
  • They complexify the constructor and destructor
  • They may cause the compiler-generated mechanism to be inserted into each object in the inherited class and break the layering of abstraction, causing inefficiency.

Note: virtual base classes are needed to solve some interface-related issues. 

To view or add a comment, sign in

More articles by Shrikant Badiger

  • NVMe Over TCP

    NVMe over TCP is enhanced feature of NVMe over Fabrics. It used the standard network stack(Ethernet) without any…

    1 Comment
  • Bazel Build for C++ Software Application

    Bazel Tool is developed by google to automate the build process. Now It's an open source and it can be used by anyone.

  • High-performance Computing in C++ : Open Muti Processing(OpenMP)

    Open Multi-Processing: Let's consider the parallelization approaches, basically, we can think of imperative…

  • High-performance Computing in C++

    Single Instruction Multiple Data (SIMD) Multiple core CPUs and Multithreading: Declarative(OpenMP), imperative…

  • vSocket Interface - Guest to Host Communication

    vSocket: VMware vSocket provides a very similar API to the Unix Socker interface for communication. vSocket library is…

  • Custom Memory Management in C++

    Memory Management: Process in which memory allocation and de-allocation to the variable in running program and handle…

  • Pointers in C

    Pointers in C: Pointers are fundamental parts of C Programming. Pointers provide the lots of power and flexibility in C…

  • CMake and Useful Info

    CMake is an open-source tool to build, test, and package software applications. CMake provides control over the…

    1 Comment
  • Interrupt !!

    Processors need to detect hardware activities. There are multiple solutions to detect hardware activities.

  • PXE: Preboot Execution Environment

    PXE: Preboot Execution Environment. Technology helps computers to boot up remotely through a network interface.

Others also viewed

Explore content categories