A brief discussion of memory allocation details in instances of C++ class

  • 2020-05-10 18:32:54
  • OfStack

1 class, there are member variables: static and non-static; There are three kinds of member functions: static, non-static and virtual.

So how are these things allocated in memory?

Take an example to illustrate:


#include"iostream.h"
class CObject
{
public:
  static int a;
  CObject();
  ~CObject();
  void Fun();
private: 
int m_count; 
int m_index;
};
VoidCObject::Fun(){  cout<<"Fun\n"<<endl;}
CObject::CObject(){  cout<<"Construct!\n";}
CObject::~CObject(){  cout<<"Destruct!\n";}
int CObject::a=1;
void main(){
cout<<"Sizeof(CObject):"<<sizeof(CObject)<<endl; cout<<"CObject::a="<<CObject::a<<endl;
CObject myObject;cout<<"sizeof(myObject):"<<sizeof(myObject)<<endl;
cout<<"sizeof(int)"<<sizeof(int)<<endl;
}

  this is one of my test codes, and the result is as follows:

 


Sizeof(CObject):8
CObject::a=1
Construct!
sizeof(myObject):8
sizeof(int)4
Destruct!

I have questions about   as follows:

(1)) in C++, it should be the object before the memory space is allocated, right? Why is the memory size of CObject 8, which is exactly the sum of the sizes of the two member variables 1! Does the class already have memory space before it's instantiated?

(2) when the object is generated, the calculated memory size is still 8. Does the function not occupy memory space? Should I at least put a function pointer in there? How is memory laid out?

(3) the static member should belong to the class, why does the size of the class not include the size of the static member?

The answers are as follows:

1) Sizeof(CObject) is calculated at compile time. A class is defined, and the memory occupied by it is already known by the compiler. At this time, only the size occupied by it is obtained, and no memory operation is allocated. Think of it this way: the compiler must know the size, it has nothing to do with allocating the memory space, it knows the size, and it will know how much to allocate when it instantiates it later.

2) the ordinary members of the class and static member functions do not occupy the memory of the class. As for the function pointer you mentioned, there is a pointer to the virtual function table when there are virtual functions in your class. That is to say, if there are virtual functions in your class, the value of sizeof(CObject) will increase by 4 bytes.

In fact, the member functions of the class are actually the same as ordinary global functions.

However, when the compiler compiles, it adds a parameter to the member function and passes in a pointer to the object.

The address of the member function is known globally, so there is no need to save the address of the member function in the memory space of the object.

Calls to member functions (non-virtual functions) are determined at compile time.

Calls like myObject.Fun () are compiled to look like _CObject_Fun(&myObject).

Functions are not included in sizeof because they are code that is Shared by objects, unlike data. You don't have to have a function pointer in the object, because the object doesn't have to know the addresses of its individual functions (other code calls the function instead of the object).

Class attributes refer to the data members of the class, they are instantiated when 1 object is allocated memory for the data members, and the data members of each object are opposite, while the member functions are common ~

The only difference between a static member function and a 1-like member function is that there is no this pointer, so non-static data members cannot be accessed. In short, all functions in the program are located in the code area.

3) the static member does not belong to an object, and sizeof takes the size of the object.

When you know the top, you can change it to 1 and then go down and see:

I would also like to add:


class CObject
{
public:
static int a;
CObject();
~CObject();
void Fun();
private:
double m_count; // So this is changed to double
int m_index;
};
 In this class sizeof() The measured size is  2*sizeof(double)=16
 
class CObject
{
public:
static int a;
CObject();
~CObject();
void Fun();
private:
char m_count; // So this is changed to char
int m_index;
};
 The size is 2*sizeof(int)=8
class CObject
{
public:
static int a;
CObject();
~CObject();
void Fun();
private:
double m_count; // So this is changed to double
int m_index;
char c;
};
sizeof(char)+sizeof(int) <sizeof(double)  So the magnitude is 2*sizeof(double)

Actually, there's another memory alignment problem.

The empty class size is 1.

Some other questions to pay attention to:

How much space does an empty class take up?


class Base 
{ 
public: 
  Base(); 
  ~Base(); 
}; 
 class Base {
 public:
Base();
 ~Base();
 };

Notice that I've declared the construct and destruct here, but sizeof(Base) is 1.

Since an empty class also needs to be instantiated, an instantiation of a class allocates an address in memory, and each instance has a unique address in memory. The empty class is also instantiated, so the compiler implicitly adds a byte to the empty class so that it has a unique address. So sizeof for an empty class is 1.

With the constructor and destructor, the member function, is has nothing to do with sizeof, also it is not difficult to understand because our sizeof is for instance, while the common member function, is aimed at the class body, a class member function, multiple instances also share the same function pointer, so naturally can't return for instance, the size of the it has mentioned in my other 1 blog post.

Next, look at the following piece of code


class Base 
{ 
public: 
  Base();         
  virtual ~Base();     // Each instance has a table of virtual functions  
  void set_num(int num)  //  Common member function, public for each instance, does not belong to sizeof statistical  
  { 
    a=num; 
  } 
private: 
  int a;         // Account for 4 byte  
  char *p;         //4 Byte Pointers  
}; 
  
class Derive:public Base 
{ 
public: 
  Derive():Base(){};   
  ~Derive(){}; 
private: 
  static int st;     // Non-instance exclusive  
  int d;           // Account for 4 byte  
  char *p;          //4 Byte Pointers  
};  
int main() 
{ 
  cout<<sizeof(Base)<<endl; 
  cout<<sizeof(Derive)<<endl; 
  return 0; 
} 
 class Base {
 public:
Base();
virtual ~Base(); // Each instance has a table of virtual functions 
void set_num(int num) { a=num; } // Common member function, public for each instance, does not belong to sizeof statistical 
private:
 int a; // Account for 4 byte 
char *p; //4 Byte Pointers 
};
class Derive:public Base {
public:
Derive():Base(){};
~Derive(){};
private:
static int st; // Non-instance exclusive 
int d; // Account for 4 byte 
char *p; //4 Byte Pointers 
};
int main() {
cout<<sizeof(Base)<<endl;
cout<<sizeof(Derive)<<endl;
 return 0;
}

As a result, of course,

12

20

int   a; char * p; That's 8 bytes.

The virtual destructor virtual ~Base(); Is 4 child bytes.

Other member functions are not included in the sizeof statistics.

The Derive class first has the part of the Base class, which is 12 bytes.

int   d; char * p; Account for 8 bytes

static int st; Not included in sizeof statistics

So 1 is a total of 20 bytes.

Consider adding one member to Derive.


class Derive:public Base
 
{
 
public:
 
  Derive():Base(){};
 
  ~Derive(){};
 
private:
 
  static int st;
 
  int d;
 
  char *p;
 
  char c;
 
};
 
 class Derive:public Base {
 
public:
 
 Derive():Base(){};
 
 ~Derive(){};
 
private:
 
static int st;
 
 int d;
 
char *p;
 
 char c;
 
};

At this point, the result becomes

12

24

1 char c; The addition of 4 bytes indicates that the size of the class is also subject to a similar class byte alignment and complement rule.

So far, we can summarize the following principles:

1. The size of the class is the sum of the type sizes of the non-static member data of the class, that is, the static member data is not considered.

2. The ordinary member function has nothing to do with sizeof.


Related articles: