How do you determine the size of an object in C++?

113,011

Solution 1

To a first order approximation, the size of an object is the sum of the sizes of its constituent data members. You can be sure it will never be smaller than this.

More precisely, the compiler is entitled to insert padding space between data members to ensure that each data member meets the alignment requirements of the platform. Some platforms are very strict about alignment, while others (x86) are more forgiving, but will perform significantly better with proper alignment. So, even the compiler optimization setting can affect the object size.

Inheritance and virtual functions add an additional complication. As others have said, the member functions of your class themselves do not take up "per object" space, but the existence of virtual functions in that class's interface generally implies the existence of a virtual table, essentially a lookup table of function pointers used to dynamically resolve the proper function implementation to call at runtime. The virtual table (vtbl) is accessed generally via a pointer stored in each object.

Derived class objects also include all data members of their base classes.

Finally, access specifiers (public, private, protected) grant the compiler certain leeway with packing of data members.

The short answer is that sizeof(myObj) or sizeof(MyClass) will always tell you the proper size of an object, but its result is not always easy to predict.

Solution 2

sizeof(Temp)

will give you the size. Most likely, it is 4 bytes (given a whole lot of assumptions) and that is only for the int. The functions do not take up any room on a per object basis, they are compiled once, and linked by the compiler each time they are used.

It's impossible to say exactly what the object layout is, however, the standard doesn't define the binary representation for objects.

There are a few things to be aware of with binary representations, like they aren't necessarily the sum of the bytes of the data members, due to things like structure padding

Solution 3

I've always wondered this sort of thing, so I decided to come up with a complete answer. It's about what you might expect, and it is predictable (yay)! Thus, with the information below, you ought to be able to predict the size of a class.

Using Visual Studio Community 2017 (Version 15.2), in Release mode with all optimizations disabled and RTTI (Run-time Type Information) off, I have determined the following:


Shortish answer:

First of all:

  • In 32 (x86) bit, <size of pointer> == 4 bytes
  • In 64 (x64) bit, <size of pointer> == 8 bytes
  • When I say "virtual class inheritance", I mean e.g.: class ChildClass: virtual public ParentClass

Now, my findings are that:

  • empty classes are 1 byte
  • inheritance of an empty class is still 1 byte
  • empty classes with functions are still 1 byte (?! see Note below for explanation)
  • inheritance of an empty class with a function is still 1 byte
  • adding a variable to an empty class is <size of variable> bytes
  • inheriting a class with a variable and adding another variable is <size of variables> bytes
  • inheriting a class and overriding its function adds a vtable (further explanation provided in Conclusions section) and is <size of pointer> bytes
  • simply declaring a function virtual also adds a vtable, making it <size of pointer> bytes
  • virtual class inheritance of an empty class (with or without a member function) also adds a vtable, and makes the class <size of pointer> bytes
  • virtual class inheritance of a non-empty class also adds a vtable, but it gets somewhat complicated: it adds <size of pointer> bytes to the total, wrapping all of the member variables in as many <size of pointer> bytes increments as is necessary to cover <total size of member variables> - yeah, you read that right... (see my guess as to what's going on in Conclusions...)

Note that I even tried having the function() cout some text, creating an instance of the class, and calling the function; it doesn't change the size of the function class (it's not an optimization)! I was somewhat surprised, but it actually makes sense: member functions don't change, so they can be stored external to the class itself.

Conclusions:

  • Empty classes are 1 byte, since that is the minimum required for it to have a presence in memory. Once data or vtable data is added, though, begin counting at 0 bytes.
  • Adding a (non-virtual) member function does nothing to the size, because the member function is stored externally.
  • Declaring a member function to be virtual (even if the class is not overridded!) or overriding a member function in a child class adds what is called a "vtable" or "virtual function table", which allows for Dynamic Dispatch (which is really super awesome to use though and I highly recommend using it). This vtable consumes <size of pointer> bytes, adding <size of pointer> bytes to said class. This vtable can only exist once per class (either it does or it doesn't), of course.
  • Adding a member variable increases the size of the class by that member variable, regardless of whether said member variable is in the parent or child class (the parent class remains its own size though, of course).
  • Virtual class inheritance is the only part that gets complicated... So... I think what's going on after a little experimentation is: the size of the class actually increments in <size of pointer> bytes at a time, even if it doesn't need to consume that much memory, I'm guessing because it's adding a vtable "helper block" for each <size of pointer> bytes of memory or something...

Long answer:

I determined all of this using this code:

#include <iostream>

using namespace std;

class TestA
{

};

class TestB: public TestA
{

};

class TestC: virtual public TestA
{

};

class TestD
{
    public:
        int i;
};

class TestE: public TestD
{
    public:
        int j;
};

class TestF: virtual public TestD
{
    public:
        int j;
};

class TestG
{
    public:
        void function()
        {

        }
};

class TestH: public TestG
{
    public:
        void function()
        {

        }
};

class TestI: virtual public TestG
{
    public:
        void function()
        {

        }
};

class TestJ
{
    public:
        virtual void function()
        {

        }
};

class TestK: public TestJ
{
    public:
        void function() override
        {

        }
};

class TestL: virtual public TestJ
{
    public:
        void function() override
        {

        }
};

void main()
{
    cout << "int:\t\t" << sizeof(int) << "\n";
    cout << "TestA:\t\t" << sizeof(TestA) << "\t(empty class)\n";
    cout << "TestB:\t\t" << sizeof(TestB) << "\t(inheriting empty class)\n";
    cout << "TestC:\t\t" << sizeof(TestC) << "\t(virtual inheriting empty class)\n";
    cout << "TestD:\t\t" << sizeof(TestD) << "\t(int class)\n";
    cout << "TestE:\t\t" << sizeof(TestE) << "\t(inheriting int + int class)\n";
    cout << "TestF:\t\t" << sizeof(TestF) << "\t(virtual inheriting int + int class)\n";
    cout << "TestG:\t\t" << sizeof(TestG) << "\t(function class)\n";
    cout << "TestH:\t\t" << sizeof(TestH) << "\t(inheriting function class)\n";
    cout << "TestI:\t\t" << sizeof(TestI) << "\t(virtual inheriting function class)\n";
    cout << "TestJ:\t\t" << sizeof(TestJ) << "\t(virtual function class)\n";
    cout << "TestK:\t\t" << sizeof(TestK) << "\t(inheriting overriding function class)\n";
    cout << "TestL:\t\t" << sizeof(TestL) << "\t(virtual inheriting overriding function class)\n";

    cout << "\n";
    system("pause");
}

Output:

32 (x86) bits:

int:            4
TestA:          1       (empty class)
TestB:          1       (inheriting empty class)
TestC:          4       (virtual inheriting empty class)
TestD:          4       (int class)
TestE:          8       (inheriting int + int class)
TestF:          12      (virtual inheriting int + int class)
TestG:          1       (function class)
TestH:          1       (inheriting function class)
TestI:          4       (virtual inheriting function class)
TestJ:          4       (virtual function class)
TestK:          4       (inheriting overriding function class)
TestL:          8       (virtual inheriting overriding function class)

64 (x64) bits:

int:            4
TestA:          1       (empty class)
TestB:          1       (inheriting empty class)
TestC:          8       (virtual inheriting empty class)
TestD:          4       (int class)
TestE:          8       (inheriting int + int class)
TestF:          24      (virtual inheriting int + int class)
TestG:          1       (function class)
TestH:          1       (inheriting function class)
TestI:          8       (virtual inheriting function class)
TestJ:          8       (virtual function class)
TestK:          8       (inheriting overriding function class)
TestL:          16      (virtual inheriting overriding function class)

If you want information on multiple inheritance, go figure it out your darn self! -.-

Solution 4

If you want detailed information about how objects are represented in memory at run-time, the ABI (Application Binary Interface) specification is the place to look. You'll need to look determine which ABI your compiler implements; for example, GCC versions 3.2 and above implement the Itanium C++ ABI.

Solution 5

Methods belong to the class, not any particular instantiated object.

Unless there are virtual methods, the size of an object is the sum of the size of its non-static members, plus optional padding between the members for alignment. The members will probably be laid out sequentially in memory, but the spec doesn't guarantee ordering between sections with different access specifications, nor ordering relative to the layout of superclasses.

With virtual methods present, there may be additional space taken for vtable and other RTTI information.

On most platforms, executable code goes in the read-only .text (or similarly named) section of the executable or library, and is never copied anywhere. When class Temp has a method public: int function1(int), the Temp metadata may have a pointer to a _ZN4Temp9function1Ei (mangled name may be different depending on compiler) function for the actual implementation, but certainly it would never contain the executable code embedded.

Share:
113,011
Joel
Author by

Joel

Updated on August 19, 2020

Comments

  • Joel
    Joel almost 4 years

    For example, say I have a class Temp:

    class Temp
    {
        public:
            int function1(int foo) { return 1; }
            void function2(int bar) { foobar = bar; }
    
        private:
            int foobar;
    };
    

    When I create an object of class Temp, how would I calculate how much space it needs, and how is it represented in memory (e.g.| 4 bytes for foobar| 8 bytes for function1 | etc | )

  • jogojapan
    jogojapan almost 11 years
    Welcome to Stackoverflow. Your answer has the same content as e.g. that by Drew Hall, except that you leave out essential parts, such as about padding and about the effect of polymorphism. If you give an answer to an already answered question, make sure you answer improves on the existing ones.
  • KeyC0de
    KeyC0de over 5 years
    "the member functions of your class themselves do not take up "per object" space". True, but even if they did, those are still functions, not data so they don't take any measurable space that sizeof(myObject) could account for.
  • aafulei
    aafulei almost 4 years
    Very helpful answer. One suggestion is that when you say [s]ome platforms are very strict about alignment it might be even more helpful if you could give one example, as you give x86 to illustrate a forgiving platform.
  • John
    John almost 2 years
    @Drew Hall access specifiers (public, private, protected) grant the compiler certain leeway with packing of data membersCould you please explain that in more details? Maybe a simple example helps.