C + + Notes general new operator and positioning new operator

Posted by cgchris99 on Sun, 05 Dec 2021 17:59:55 +0100

Usually, new is responsible for finding a memory block that can meet the requirements in the heap. There is another variant of the new operator, called the placement new operator, which allows you to specify the location to use. Programmers can use this feature to set their memory management procedures, deal with hardware that needs to be accessed through a specific address, or create objects in a specific location.

To use the new property,

  • The header file new needs to be included, which provides the prototype of this version of the new operator;
  • Use the new operator to provide parameters for the desired address.

The following code snippet demonstrates four uses of the new operator:

#include <new>
struct chaff {
    char dross[20];
    int slag;
};
char buffer1[50];
char buffer2[500];
int main() {
    chaff* p1, * p2;
    int* p3, * p4;
    //routine new operator
    p1 = new chaff;//The structure is placed in the heap
    p3 = new int[20];//int The array is placed in the heap
    //location new operator
    p2 = new (buffer1) chaff;//Structure on buffer1 in
    p4 = new (buffer2) int[20];//int Array on buffer2 in
    return 0;
}

The above example uses two static arrays to provide memory space for positioning the new operator. Next, use the regular new operator and the positional new operator to create a dynamically allocated array.

#include <iostream>
#include <new>
const int BUF = 512;
const int N = 5;
char buffer[BUF];
int main() {
    using namespace std;
    double* pd1, * pd2;
    int i;
    cout << "Calling new and placement new:\n";
    pd1 = new double[N];
    pd2 = new (buffer) double[N];
    for (i = 0; i < N; i++) {
        pd2[i] = pd1[i] = 1000 + 20.0 * i;
    }
    cout << "Memory adress:\n" << "heap:" << pd1 << " static:" << pd2 << endl;
    cout << "Memory contents:\n";
    for (i = 0; i < N; i++) {
        cout << pd1[i] << " at " << pd1 + i << ";";
        cout << pd2[i] << " at " << pd2 + i << endl;
    }
    //----------------again---------------------------
    cout << "Calling new and placement new a second time:\n";
    double* pd3, * pd4;
    pd3 = new double[N];
    pd4 = new (buffer) double[N];
    for (i = 0; i < N; i++) {
        pd4[i] = pd3[i] = 1000 + 40.0 * i;
    }
    cout << "Memory adress:\n" << "heap:" << pd3 << " static:" << pd4 << endl;
    cout << "Memory contents:\n";
    for (i = 0; i < N; i++) {
        cout << pd3[i] << " at " << pd3 + i << ";";
        cout << pd4[i] << " at " << pd4 + i << endl;
    }
    //release p1 Point to the memory block, in buffer Allocate new memory in
    cout << "Calling new and placement new a third time:\n";
    delete[] pd1;
    pd1 = new double[N];
    pd2 = new (buffer + N * sizeof(double)) double[N];
    for (i = 0; i < N; i++) {
        pd2[i] = pd1[i] = 1000 + 60.0 * i;
    }
    cout << "Memory adress:\n" << "heap:" << pd1 << " static:" << pd2 << endl;
    cout << "Memory contents:\n";
    for (i = 0; i < N; i++) {
        cout << pd1[i] << " at " << pd1 + i << ";";
        cout << pd2[i] << " at " << pd2 + i << endl;
    }
    delete[] pd1;
    delete[] pd3;
}

Operation results:

 

be careful:

1. The addresses of p2 and buffer are the same, indicating that array p2 is indeed placed in array buffer. p1 is in a dynamically managed heap.

2. Since buffer is a char pointer, when cout is applied to the char pointer, it will start printing from the first character until it encounters a null character. Therefore, (void *) is used to force the conversion of buffer input.

3. Locate the new operator, use the address passed to it, do not track which memory units are used, and do not look for unused memory blocks. Therefore, the second positioning of the new operator allocates the same memory block as the first time. This places some of the burden of memory management on programmers.

//The offset is 40 bytes
pd2 = new (buffer + N * sizeof(double)) double[N];

4. After creating a new class object by locating the new operator, when the object dies, the program will not automatically call its destructor, so the destructor must be explicitly called. This is one of the few situations where the destructor needs to be called.

In the above program, delete is not used to free the memory allocated by the positioning new operator. The memory specified by buffer is static memory, and delete is only used for pointers to the heap memory allocated by conventional new. That is, the array buffer is outside the jurisdiction of delete. The following statement will cause a runtime error:

delete [] pd2;//won't work

If the buffer is created using the regular new operator, you can use the regular delete operator to free up the entire memory block.

char * buffer = new char[BUF];
delete [] buffer;

delete [] buffer; Releases the entire memory block allocated using the regular new operator, but does not call the destructor for the object created in the memory block by positioning the new operator.
The solution to this problem is to explicitly call the destructor for an object created using the positioned new operator. When explicitly calling the destructor, you must specify the object to destroy. Because there are pointers to objects, you can use these pointers:

String *pd = new (buffer) String("hello",5);
pd->~String();

It should be noted that objects created with the positioning new operator should be deleted in the reverse order of creation. The reason is that objects created late may depend on objects created earlier. In addition, the buffer used to store all objects can be released only after all objects have been destroyed.

five    The new operator simply returns the address passed to it and casts it to void * so that it can be assigned to any pointer type.

 

Topics: C++