<C++> gestión de memoria

1. Distribución de memoria C/C++

Primero echemos un vistazo al siguiente código.

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";
    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);
}

La siguiente es en qué área de la memoria se almacena cada parte

inserte la descripción de la imagen aquí

ilustrar:

1. La pila también se denomina pila y se utiliza para almacenar variables locales no estáticas/parámetros de función/valores de retorno, etc. La pila crece hacia abajo .
2. El segmento mapeado en memoria es un método eficiente de mapeo de E/S para cargar un banco de memoria dinámica compartida. Los usuarios pueden utilizar la interfaz del sistema para crear memoria compartida para la comunicación entre procesos.
3. El montón se utiliza para almacenar la asignación de memoria dinámica en tiempo de ejecución y el montón crece hacia arriba .
4. El segmento de datos también se denomina área estática y se utiliza para almacenar datos globales y datos estáticos.
5. El segmento de código también se denomina área constante y se utiliza para almacenar código ejecutable y constantes de solo lectura.

Por cierto : ¿Por qué se dice que la pila crece hacia abajo, pero el montón crece hacia arriba?

En pocas palabras, en circunstancias normales, cuando se abre espacio en el área de la pila, la dirección del espacio abierto primero es mayor, mientras que cuando se abre espacio en el área del montón, la dirección del espacio abierto primero es menor.

Nota : Cuando se abre espacio en el área del montón, la dirección del espacio abierto más tarde no es necesariamente mayor que la dirección del espacio abierto primero. Porque en el área del montón, el espacio que se abre más tarde también puede estar ubicado en un espacio previamente liberado.

2. Método de gestión de memoria dinámica de C ++

El método de administración de memoria del lenguaje C puede continuar usándose en C ++, pero en algunos lugares es impotente y es más problemático de usar, por lo que C ++ ha propuesto su propio método de administración de memoria: administración dinámica de memoria mediante operadores nuevos y de eliminación .

tipos integrados de operaciones nuevas/eliminadas

1. Solicitar dinámicamente un único espacio de un determinado tipo

//动态申请单个int类型的空间
int* p1 = new int; //申请

delete p1; //销毁

Su función es equivalente a:

//动态申请单个int类型的空间
int* p2 = (int*)malloc(sizeof(int)); //申请

free(p2); //销毁

2. Solicite dinámicamente múltiples espacios de un determinado tipo.

//动态申请10个int类型的空间
int* p3 = new int[10]; //申请

delete[] p3; //销毁

Su función es equivalente a:

//动态申请10个int类型的空间
int* p4 = (int*)malloc(sizeof(int)* 10); //申请

free(p4); //销毁

3. Solicite dinámicamente un único espacio de un determinado tipo e inicialícelo

//动态申请单个int类型的空间并初始化为10
int* p5 = new int(10); //申请 + 赋值

delete p5; //销毁

Su función es equivalente a:

//动态申请一个int类型的空间并初始化为10
int* p6 = (int*)malloc(sizeof(int)); //申请
*p6 = 10; //赋值

free(p6); //销毁

4. Solicite dinámicamente múltiples espacios de un determinado tipo e inicialícelos

//动态申请10个int类型的空间并初始化为0到9
int* p7 = new int[10]{
    
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //申请 + 赋值

delete[] p7; //销毁

Su función es equivalente a:

//动态申请10个int类型的空间并初始化为0到9
int* p8 = (int*)malloc(sizeof(int)* 10); //申请
for (int i = 0; i < 10; i++) //赋值
{
    
    
	p8[i] = i;
}

free(p8); //销毁

Nota: Para solicitar y liberar el espacio de un solo elemento, use los operadores nuevo y eliminar, para solicitar y liberar espacio continuo, use nuevo [] y eliminar [], nota: utilícelos juntos.

tipo personalizado de operación nueva/eliminar

Hay las siguientes clases:

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

private:
    int _a;
};

1. Solicite dinámicamente una sola clase

A *p1 = new A();//动态申请单个类
delete p1;

//等价于
A *p2 = (A *) malloc(sizeof(A));
free(p2);

2. Solicite dinámicamente múltiples clases

A *p5 = new A[10];//动态申请多个类
delete[] p5;

//等价于
A *p6 = (A *) malloc(sizeof(A) * 10);
free(p6);

Nota : Al solicitar un tipo de espacio personalizado, new llamará al constructor, eliminar llamará al destructor , pero malloc y free no.

en conclusión:

1. En C++, si solicita un objeto o matriz de tipo integrado, no hay diferencia entre usar new/delete y malloc/free.
2. Si es un tipo personalizado, hay una gran diferencia: nuevo y eliminar abren espacio + constructor, destructor + liberan espacio respectivamente, mientras que malloc y free solo abren y liberan espacio.
3. Se recomienda utilizar nuevo y eliminar tanto como sea posible en C ++, ya sea la aplicación y el lanzamiento de tipos integrados o tipos personalizados.

3. Funciones de operador nuevo y operador de eliminación

newy deleteson operadores para que los usuarios apliquen y liberen memoria dinámica, operator newy operator deleteson funciones globales proporcionadas por el sistema. Las funciones globales se newllaman en la capa inferior operator newpara solicitar espacio y el espacio se libera a través de funciones globales deleteen la capa inferior .operator delete

int* p1 = (int*)operator new(sizeof(int)* 10); //申请
operator delete(p1); //销毁

//等价于
int* p2 = (int*)malloc(sizeof(int)* 10); //申请
free(p2); //销毁

operator newDe hecho, también se utiliza mallocpara solicitar espacio. Si mallocla solicitud de espacio tiene éxito, regresará directamente. De lo contrario, se implementarán las contramedidas proporcionadas por el usuario por espacio insuficiente. Si el usuario proporciona esta medida, continúe solicitando. de lo contrario, se generará una excepción. operator deleteEn última instancia, se trata freede liberar espacio.

inserte la descripción de la imagen aquí

Nota : Aunque dichas operator newy operator deleteson funciones globales proporcionadas por el sistema, también podemos sobrecargar sus operator newfunciones exclusivas y de eliminación de operadores para una determinada clase para mejorar la eficiencia.

4. Principio de implementación de nuevo y eliminar.

tipo incorporado

Si está solicitando un tipo de espacio integrado, new/delete es básicamente similar a malloc/free. La diferencia es que la aplicación new/delete libera el espacio de un solo elemento, y new[]/delete[ ] La aplicación libera espacio continuo. Además, si la aplicación malloc falla, se devolverá NULL y si la nueva aplicación falla, se generará una excepción.

tipo personalizado

El principio de lo nuevo
 1. Llame al operador de nueva función para solicitar espacio.
 2. Ejecute el constructor en el espacio solicitado para completar la construcción del objeto.

El principio de eliminación
 1. Ejecute el destructor en el espacio para completar la limpieza de recursos en el objeto.
 2. Llame a la función de eliminación del operador para liberar el espacio del objeto.

El principio de la nueva T [N]
 1. Llame a la función del operador nuevo [] y, de hecho, llame a la función del operador nueva en la función del operador nuevo [] para completar la solicitud de N espacios de objetos.
 2. Ejecute el constructor N veces en el espacio solicitado.

El principio de eliminar []
 1. Ejecute el destructor N veces en el espacio para completar la limpieza de recursos en N objetos.
 2. Llame a la función de eliminación del operador [] y, de hecho, llame a la función de eliminación del operador en la función de eliminación del operador [] para completar la liberación de N espacios de objetos.

5. Localiza la nueva expresión.

Las nuevas expresiones posicionadas son un uso avanzado en C++ que le permiten crear objetos en ubicaciones de memoria específicas en lugar de la asignación de montón predeterminada. Esto es útil en algunos casos especiales, como implementar grupos de memoria en regiones de memoria específicas o crear objetos en memoria compartida, etc.

Ejemplo:

#include <iostream>

class MyClass {
    
    
public:
    MyClass(int value) : data(value) {
    
    
        std::cout << "构造函数: " << data << std::endl;
    }

    ~MyClass() {
    
    
        std::cout << "析构函数: " << data << std::endl;
    }

private:
    int data;
};

int main() {
    
    
    // 分配一块内存
    void* memory = operator new(sizeof(MyClass));

    // 在预分配的内存上使用定位new表达式创建对象
    MyClass* obj = new (memory) MyClass(42);

    // 手动调用析构函数
    obj->~MyClass();

    // 释放预分配的内存
    operator delete(memory);

    return 0;
}

Nota : Antes de usar la nueva expresión de posicionamiento para llamar explícitamente al constructor para la inicialización, el espacio aplicado por el operador nuevo no puede considerarse como un objeto, es solo un espacio con el mismo tamaño que el objeto A, porque el constructor aún no ha sido ejecutado.

6. Preguntas frecuentes

¿La diferencia entre malloc/free y new/delete?

Lo que malloc/free y new/delete tienen en común es que todos solicitan espacio del montón y el usuario debe liberarlos manualmente.

Las diferencias son:

  1. malloc y free son funciones, new y eliminar son operadores
  2. El espacio solicitado por malloc no se inicializará, pero se puede inicializar uno nuevo.
  3. Cuando malloc solicita espacio, es necesario calcular manualmente el tamaño del espacio y transmitirlo. Nuevo solo debe ir seguido del tipo de espacio. Si hay varios objetos, especifique el número de objetos en []
  4. El valor de retorno de malloc es void *, que debe forzarse cuando se usa, y new no lo necesita, porque new va seguido del tipo de espacio
  5. Cuando malloc no solicita espacio, devuelve NULL, por lo que debe considerarse vacío al usarlo. New no lo necesita, pero New necesita detectar excepciones.
  6. Al solicitar un tipo de objeto personalizado, malloc/free solo abrirá espacio y no llamará al constructor ni al destructor, mientras que new llamará al constructor para completar la inicialización del objeto después de solicitar espacio, y eliminar llamará al destructor antes de liberar el espacio Completa la limpieza de recursos en el espacio

¿Qué es una pérdida de memoria y el daño de una pérdida de memoria?

¿Qué es una pérdida de memoria? Una pérdida de memoria se refiere a una situación en la que un programa no puede liberar memoria que ya no está en uso debido a negligencia o error. Una pérdida de memoria no se refiere a la desaparición física de la memoria, sino más bien a la pérdida de control sobre un determinado segmento de memoria debido a un error de diseño después de que la aplicación asigna un determinado segmento de memoria, lo que resulta en un desperdicio de memoria.

Peligros de pérdidas de memoria: las pérdidas de memoria ocurren en programas de larga duración, que tienen un gran impacto, como sistemas operativos, servicios en segundo plano, etc. Las pérdidas de memoria darán lugar a respuestas cada vez más lentas y, finalmente, se congelarán.

void MemoryLeaks() {
    
    
    // 1.内存申请了忘记释放
    int *p1 = (int *) malloc(sizeof(int));
    int *p2 = new int;

    // 2.异常安全问题
    int *p3 = new int[10];

    Func();// 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.

    delete[] p3;
}

Clasificación de pérdidas de memoria.

En los programas C/C++, generalmente nos preocupamos por dos aspectos de las pérdidas de memoria:

  • Pérdida de memoria del montón (pérdida de memoria del montón)

La memoria del montón se refiere a una parte de la memoria asignada del montón durante la ejecución del programa según sea necesario y debe llamarse o eliminarse malloc/calloc/realloc/newdespués de su uso . Suponiendo que el error de diseño del programa hace que esta parte de la memoria no se libere, esta parte del espacio ya no se utilizará en el futuro y se producirá una pérdida de montón .freedelete

  • fuga de recursos del sistema

Se refiere a los recursos asignados por el sistema utilizado por el programa, como sockets, descriptores de archivos, canalizaciones, etc., que no se liberan utilizando las funciones correspondientes, lo que resulta en un desperdicio de recursos del sistema, lo que puede conducir seriamente a una reducción del rendimiento del sistema y ejecución inestable del sistema.

¿Cómo evitar pérdidas de memoria?

1. Buenas especificaciones de diseño en la etapa inicial del proyecto, desarrolle buenos estándares de codificación y recuerde liberar el espacio de memoria correspondiente.
2. Utilice ideas RAII o sugerencias inteligentes para gestionar recursos.
3. Algunas empresas utilizan internamente bibliotecas de administración de memoria privadas implementadas internamente, que tienen opciones integradas de detección de fugas de memoria.
4. Si algo sale mal, utilice la herramienta de pérdida de memoria para detectarlo.

o deleteeliminar. Suponiendo que el error de diseño del programa hace que esta parte de la memoria no se libere, esta parte del espacio ya no se utilizará en el futuro y se producirá una pérdida de montón .

  • fuga de recursos del sistema

Se refiere a los recursos asignados por el sistema utilizado por el programa, como sockets, descriptores de archivos, canalizaciones, etc., que no se liberan utilizando las funciones correspondientes, lo que resulta en un desperdicio de recursos del sistema, lo que puede conducir seriamente a una reducción del rendimiento del sistema y ejecución inestable del sistema.

¿Cómo evitar pérdidas de memoria?

1. Buenas especificaciones de diseño en la etapa inicial del proyecto, desarrolle buenos estándares de codificación y recuerde liberar el espacio de memoria correspondiente.
2. Utilice ideas RAII o sugerencias inteligentes para gestionar recursos.
3. Algunas empresas utilizan internamente bibliotecas de administración de memoria privadas implementadas internamente, que tienen opciones integradas de detección de fugas de memoria.
4. Si algo sale mal, utilice la herramienta de pérdida de memoria para detectarlo.

Supongo que te gusta

Origin blog.csdn.net/ikun66666/article/details/132510106
Recomendado
Clasificación