[C++] An article that takes you to understand dynamic memory management in C++, new allows everyone to have objects

 

Table of contents

1. C/C++ memory distribution

2. Dynamic memory management methods in C language: malloc, calloc, realloc

3. C++ memory management method

3.1 new/delete operation built-in type

3.2 new and delete operation custom type

3.3 The exception handling mechanism of malloc and new

4. Operator new and operator delete functions

4.1 operator new and operator delete functions

4.1.1 operator new source code

4.1.2 operator delete source code

5. Implementation principle of new and delete

5.1 Built-in types

5.2 Custom types

5.2.1 The principle of new

5.2.2 The principle of delete

5.2.3 Principle of new T[N]

5.2.4 The principle of delete[]

6. Position new expression (understand)

7. The difference between malloc/free and new/delete

7.1 Malloc/free is the same as new/delete

7.2 Differences between malloc/free and new/delete


1. C/C++ memory distribution

Let's first understand several areas of C/C++ memory allocation. Take the following code as an example:

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
    static int staticVar = 1;
    int localVar = 1;
    int num1[10] = { 1, 2, 3, 4 };
    char char2[] = "abcd";
    const char* pChar3 = "abcd";
    int* ptr1 = (int*)malloc(sizeof(int) * 4);
    int* ptr2 = (int*)calloc(4, sizeof(int));
    int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
    free(ptr1);
    free(ptr3);
}

illustrate:

1. The stack is also called the stack-non-static local variables/function parameters/return values, etc. The stack grows downward.
2. The memory-mapped segment is an efficient I/O mapping method for loading a shared dynamic memory bank. Users can use the system interface to create shared shared memory for inter-process communication.

3. The heap is used for dynamic memory allocation when the program is running, and the heap can grow upwards.
4. Data segment/static area --store global data and static data.
5. Code segment/constant area --executable code/read-only constant.

2. Dynamic memory management methods in C language: malloc, calloc, realloc

malloc: apply for a space in the memory, return the pointer of the memory block if successful, and return a null pointer (NULL) if it fails;

calloc: apply for a space in the memory, and initialize it to 0 byte by byte, return the pointer of the memory block if successful, and return a null pointer (NULL) if it fails;

realloc: Adjust the dynamically allocated memory size.

Let's take a look at the code example:

int mian()
{
	int* p1 = (int*)malloc(sizeof(int));

	int* p2 = (int*)realloc(p1, sizeof(int) * 10);// 扩容,若是异地扩容realloc会将p1的内容
	free(p2);									  // 拷贝到p2开出的内存中并释放掉p1的内存

	int* p3 = (int*)calloc(1, sizeof(int));// 开辟四个字节空间,并初始化为 1
	free(p3);
	
	return 0;
}

3. C++ memory management method

The C language memory management method can continue to be used in C++, but it is powerless in some places, and it is more troublesome to use, so C++ has proposed its own memory management method: dynamic memory management through new and delete operators .

3.1 new/delete operation built-in type

int main()
{
	// 管理对象
	// 动态开辟一个int类型的空间
	int* p1 = new int;
	// 动态开辟一个int类型的空间,并初始化为1
	int* p2 = new int(1);

	//管理对象数组
	// 动态开辟一个int类型的数组
	int* p3 = new int[10];
	// 动态开辟一个int类型的数组,并初始化
	int* p4 = new int[10]{};// 不写初始化值,默认初始化为 0
	// 动态开辟一个int类型的数组,并初始化
	int* p5 = new int[10]{ 1,2,3 };// 前三个分别初始化为1 2 3,后面默认初始化为 0

	//释放开辟的内存
	delete p1;
	delete p2;
	delete[] p3;// 对应释放数组加[]
	delete[] p4;
	delete[] p5;

	return 0;
}

Let's start the monitoring window to see the result:

Note: To apply for and release the space of a single element, use the new and delete operators, to apply for and release continuous space, use new[] and delete[], note: use them together.

3.2 new and delete operation custom type

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};

int main()
{
	A* a1 = (A*)malloc(sizeof(A));

	A* a2 = new A;

	free(a1);

	delete(a2);

	return 0;
}

Let's take a look at malloc, new to open up dynamic memory and free, delete to release memory.

Let's look at the results first:

Here, the constructor and destructor are automatically called once respectively. Who is calling here?

For the C language, there is no constructor, so the malloc designed by the C language will not automatically call the constructor, and free will not call the destructor, but for C++, it is object-oriented, new and delete will call the constructor and destructor when allocating and freeing memory.

Note: When applying for a custom type of space, new will call the constructor, delete will call the destructor, but malloc and free will not.

3.3 The exception handling mechanism of malloc and new

int main()
{
	int* p1 = (int*)malloc(1024 * 1024 * 1024);
	cout << p1 << endl;

	int* p2 = (int*)malloc(1024 * 1024 * 1024);
	cout << p2 << endl;

	try
	{
		char* p3 = new char[0x7fffffff];
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}

Returns a null pointer when malloc fails, and throws an exception if new fails.
After new fails, it will jump directly to the exception capture statement, and the code behind new will not be executed. If we do not catch the exception, the program will terminate. The exception capture will only be in try and catch, and the exception will not be caught if it is not in it.

4. Operator new and operator delete functions

4.1 operator new and operator delete functions

The C++ standard library provides operator new and operator delete functions, but these two functions are not overloaded , but their names are like overloading. They are two global functions provided by the system. new calls the operator new global function at the bottom layer to apply for space. delete uses the operator delete global function at the bottom to free up space.

Let's look at the source code separately:

4.1.1 operator new source code

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
    // try to allocate size bytes
    void *p;
    while ((p = malloc(size)) == 0)
    if (_callnewh(size) == 0)
    {
        // report no memory
        // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
        static const std::bad_alloc nomem;
        _RAISE(nomem);
    }
    return (p);
}

operator new encapsulates malloc, if it fails, an exception will be thrown.

4.1.2 operator delete source code

void operator delete(void *pUserData)
{
    _CrtMemBlockHeader * pHead;
    RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
    if (pUserData == NULL)
    return;
    _mlock(_HEAP_LOCK); /* block other threads */
    __TRY
    /* get a pointer to memory block header */
    pHead = pHdr(pUserData);
    /* verify block type */
    _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
    _free_dbg( pUserData, pHead->nBlockUse );
    __FINALLY
    _munlock(_HEAP_LOCK); /* release other threads */
    __END_TRY_FINALLY
    return;
}

/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

Summary: Through the implementation of the above two global functions, we know that operator new actually applies for space through malloc. If malloc successfully applies for space, it returns directly. Otherwise, it executes the countermeasures provided by the user for insufficient space. If the user provides this measure, it continues to apply. Otherwise an exception is thrown. operator delete finally releases space through free.

5. Implementation principle of new and delete

5.1 Built-in types

If you apply for a built-in type of space, new and malloc, delete and free are basically similar, the difference is: new/delete applies for and releases the space of a single element, new[] and delete[] apply for continuous space, Moreover, new will throw an exception when it fails to apply for space, and malloc will return NULL.

5.2 Custom types

5.2.1 The principle of new

1. Call the operator new function to apply for space
2. Execute the constructor on the applied space to complete the construction of the object

5.2.2 The principle of delete

1. Execute the destructor on the space to complete the cleanup of resources in the object
2. Call the operator delete function to release the space of the object

5.2.3 Principle of new T[N]

1. Call the operator new[] function, and actually call the operator new function in operator new[] to complete the application for N object spaces
2. Execute the constructor N times on the applied space

5.2.4 The principle of delete[]

1. Execute N times of destructors on the released object space to complete the cleanup of resources in N objects
2. Call operator delete[] to release space, and actually call operator delete in operator delete[] to release space

6. Position new expression (understand)

Positioning the new expression is to call the constructor to initialize an object in the allocated original memory space.
Format:
new (place_address) type or new (place_address) type(initializer-list)
place_address must be a pointer, and initializer-list is the initialization list of the type

scenes to be used:

Positioning new expressions are generally used in conjunction with memory pools in practice. Because the memory allocated by the memory pool is not initialized, if it is an object of a custom type, it needs to use the definition expression of new to explicitly call the constructor for initialization.

class A
{
public:
    A(int a = 0)
    : _a(a)
    {
    cout << "A():" << this << endl;
    }
    ~A()
    {
    cout << "~A():" << this << endl;
    }
private:
	int _a;
};
int main()
{
    // p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没
    有执行
    A* p1 = (A*)malloc(sizeof(A));
    new(p1)A; // 显示调用构造函数 注意:如果A类的构造函数有参数时,此处需要传参
    p1->~A();
    free(p1);

    A* p2 = (A*)operator new(sizeof(A));
    new(p2)A(10);// 显示调用构造函数
    p2->~A();
    operator delete(p2);
    
    return 0;
}

7. The difference between malloc/free and new/delete

7.1 Malloc/free is the same as new/delete

They all apply for space from the heap and need to be released manually by the user

7.2 Differences between malloc/free and new/delete

1. malloc and free are functions, and new and delete are operators.
2. The space requested by malloc will not be initialized, but new can be initialized
. 3. When malloc applies for space, you need to manually calculate the size of the space and pass it on. New only needs to follow it with the space If there are multiple objects, just specify the number of objects in []
4. The return value of malloc is void*, which must be converted when used, and new does not need it, because new is followed by the type of space
5 . When malloc fails to apply for space, it returns NULL, so it must be judged as empty when using it. new does not need it, but new needs to catch exceptions. 6. When applying for a custom type
object, malloc/free will only open up space and will not call the constructor and the destructor, while new will call the constructor to complete the initialization of the object after applying for space, and delete will call the destructor to complete the resource cleanup in the space before releasing the space

Guess you like

Origin blog.csdn.net/Ljy_cx_21_4_3/article/details/132262399