[C ++ in-depth analysis] 35. C ++ object model (top: object memory layout)

1 Object memory layout

class is a special struct

  • class and struct follow the same memory alignment rules
  • Member functions in class are stored separately from member variables (each object has independent member variables, and all objects share member functions in the class)

Programming experiment: object memory layout

//  35-1.cpp
#include<iostream>
using namespace std;
class A
{
    int i;
    int j;
    char c;
    double d;
public:
    void print()
    {
        cout << "i = " << i << ", "
        << "j = " << j << ", "
        << "c = " << c << ", "
        << "d = " << d << endl;
    }
};
struct B
{
    int i;
    int j;
    char c;
    double d;
};
int main()
{
    A a;
    cout << "sizeof(A) = " << sizeof(A) << endl;
    cout << "sizeof(B) = " << sizeof(B) << endl;
    
    a.print();
    B* p = reinterpret_cast<B*>(&a);		// 用于指针类型间的强制转换
    p->i = 1;
    p->j = 2;
    p->c = 'c';
    p->d = 3.5;
    a.print();
    return 0;
}
  • The member functions and member variables in the class are stored separately. What sizeof solves is the memory size of the member variable. class and struct follow the same memory alignment rules. According to the memory alignment rules, the size of A and B are both 24 bytes.
  • Point a pointer of type B of the structure to the class object a, and reinterpret the data in memory. The distribution of the data in the class and the data in the structure is exactly the same, so we can modify the data in the class object through the structure pointer.

Compile and run:

$ g++ 35-1.cpp -o 35-1
$ ./35-1
sizeof(A) = 24
sizeof(B) = 24
i = -879428416, j = 21866, c = �, d = 6.95316e-310
i = 1, j = 2, c = c, d = 3.5

The object at runtime is reduced to the form of a structure, and the member variables can be directly accessed through the memory address. The access authority keyword is invalid at runtime and is only valid at compile time.

2 The essence of calling member functions

  • Class member functions are located in the code segment
  • When calling a member function, the object address is implicitly passed as a parameter , and the member function accesses the member variable through the object address
  • C ++ syntax rules hide the transfer process of object addresses

Below we prove through programming experiments that the object address is implicitly passed as a parameter when calling a member function, and the member function accesses the member variable through the object address

First look at a simple code

// 35-2.cpp
#include<iostream>
using namespace std;
class Demo
{
public:
    Demo(int i, int j) : mi(i), mj(j) {}
    int getI() { return mi; }
    int getJ() { return mj; }
    int add(int value)
    {
        return mi + mj + value;
    }
private:
    int mi;
    int mj;
};
int main()
{
    Demo d(1, 2);
    cout << "sizeof(d) = " << sizeof(d) << endl;
    cout << "d.getI() = " << d.getI() << endl;
    cout << "d.getJ() = " << d.getJ() << endl;
    cout << "d.add(3) = " << d.add(3) << endl;
    return 0;
}

The above code is very simple, that is, the object access member function, because the transfer process of the object address is hidden, we can not see the implicit transfer of the object address when calling the member function.

Below we use the C language to simulate the above process, showing the passed object address to understand the process of the object calling the member function.

// ClassDemo.h
#ifndef _CLASSDEMO_H_
#define _CLASSDEMO_H_
typedef void Demo;
Demo* Demo_Create(int i, int j);
int Demo_GetI(Demo* pThis);
int Demo_GetJ(Demo* pThis);
int Demo_Add(Demo* pThis, int value);
void Demo_Free(Demo* pThis);
#endif
// ClassDemo.c
#include<malloc.h>
#include"ClassDemo.h"
struct ClassDemo
{
    int mi;
    int mj;
};
Demo* Demo_Create(int i, int j)
{
    struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));
    if (ret != NULL)
    {
        ret->mi = i;
        ret->mj = j;
    }
    return ret;
}
int Demo_GetI(Demo* pThis)
{
    struct ClassDemo* obj = (struct ClassDemo*)pThis;
    return obj->mi;
}
int Demo_GetJ(Demo* pThis)
{
    struct ClassDemo* obj = (struct ClassDemo*)pThis;
    return obj->mj;
}
int Demo_Add(Demo* pThis, int value)
{
    struct ClassDemo* obj = (struct ClassDemo*)pThis;
    return obj->mi + obj->mj + value;
}
void Demo_Free(Demo* pThis)
{
    free(pThis);
}
// 35-3.c
#include<stdio.h>
#include"ClassDemo.h"
int main()
{
    Demo* d = Demo_Create(1, 2);
    printf("d.mi = %d\n", Demo_GetI(d));
    printf("d.mj = %d\n", Demo_GetJ(d));
    printf("Add(3) = %d\n", Demo_Add(d, 3));
    Demo_Free(d);
    return 0;
}

The parameters of the functions Demo_Create, Demo_GetI, Demo_GetJ, Demo_Add, and Demo_Free are pointers of type void, and you can manipulate the members of the structure by forcing the type conversion to the pointer of the structure. These functions are equivalent to member functions. Here we need to show the transfer The address of the object.

The C ++ grammar rules hide the transfer process of the object address. Here we use the transfer object address displayed in the C language to simulate C ++ calling member functions.

$ gcc 35-3.c ClassDemo.c -o 35-3
$ ./35-3
d.mi = 1
d.mj = 2
Add(3) = 6

3 Summary

1. The class objects in C ++ are the same as the structure in the memory layout
2. The member variables and member functions are stored separately
3. The object address is implicitly passed as a parameter when calling the member function

Published 298 original articles · praised 181 · 100,000+ views

Guess you like

Origin blog.csdn.net/happyjacob/article/details/104399534