Gerenciamento de memória C/C++ (explicação detalhada do novo&excluir)

introdução

Na parte anterior da linguagem C, introduzimos a divisão de áreas de armazenamento na memória: incluindo espaço do kernel (o código do usuário não pode acessar), pilha, segmento de mapeamento de memória (mapeamento de arquivo, biblioteca dinâmica, mapeamento anônimo), heap, segmento de dados ( área estática), segmento de código (área constante) .
E você pode usar habilmente malloc, calloc, realloc e free para gerenciamento de memória dinâmica:
Detalhes do gerenciamento de memória dinâmica da linguagem C

Mas em C++, ao usar essas funções para gerenciamento dinâmico de memória, alguns requisitos não podem ser atendidos (como inicializar objetos ao solicitar espaço) e é mais problemático de usar. Portanto, o C++ propõe uma maneira de aplicar dinamicamente o newespaço delete:

O uso de novo e excluir

Ao contrário de malloc e free, new e delete são operadores em vez de funções .

Ao usar new e delete para solicitar espaço:
Para um único elemento, use new 类型;para solicitar um espaço de um tipo especificado e o valor da expressão é um ponteiro para esse espaço. Você pode usar parênteses após o tipo para inicializar o valor desse tipo new 类型(初始值);;
correspondentemente, você precisa usar delete 指针;o formulário para liberar o espaço desse único elemento.

Para elementos contínuos, use new 类型[num];para aplicar um espaço contínuo de tipos num, e o valor da expressão é o endereço desse espaço. Você pode usar {} depois de [num] para inicializar este espaço: new 类型[num]{};;
Da mesma forma, você precisa usar delete[] 指针;o formulário para liberar o espaço deste elemento contínuo.

tipo embutido

De acordo com as regras apresentadas acima, os seguintes códigos estão disponíveis para o espaço dinâmico de aplicativos do tipo embutido:

int main()
{
    
    

	int* p1 = new int;  
	//动态开辟一块int的空间,不初始化
	int* p2 = new int(10);  
	//动态开辟一块int的空间,初始化为10
	int* p3 = new int[10];  
	//动态开辟一块10个int的空间,不初始化
	int* p4 = new int[10]{
    
     1,2,3,4,5,6,7,8,9,0 }; 
	//动态开辟一块i10个int的空间,初始化为1,2,3,4,5,6,7,8,9,0

	delete p1;
	delete p2;
	//释放单个元素空间用delete
	delete[] p3;
	delete[] p4;
	//释放连续空间需使用delete[]

	return 0;
}

Podemos visualizar os valores no espaço solicitado por cada parte depurando:

insira a descrição da imagem aqui
insira a descrição da imagem aqui
insira a descrição da imagem aqui
insira a descrição da imagem aqui
Deve-se observar que newe delete, new []e delete[]não podem ser misturados Embora não haja problemas ao aplicar dinamicamente para tipos integrados, o programa falhará para tipos personalizados.

tipo personalizado

Para tipos integrados, usar malloc e free mal consegue atender às nossas necessidades, mas para tipos personalizados, como tipos de classe, precisamos inicializar objetos de classe ao solicitar espaço e malloc obviamente não pode atender às nossas necessidades.

Depois que new se aplica dinamicamente ao espaço, ele também chamará o construtor padrão; delete liberará o espaço aplicado dinamicamente após chamar o destruidor.
Aqui você precisa prestar atenção especial à diferença entre o espaço dos objetos de classe e o espaço de recursos nos objetos de classe :

class A
{
    
    
public:
	A(int a = 0)
		:_a(a)
	{
    
    
		_date = new char[_a + 1] {
    
    0};
		cout << "A(int a = 0)" << endl; //构造函数中打印
	}
	~A()
	{
    
    
		delete[] _date;
		_a = 0;
		cout << "~A()" << endl;  //析构函数中打印
	}
private:
	int _a;
	char* _date;
};

Por exemplo, esta classe A tem duas variáveis ​​de membro inte char*ocupa 8 bytes. Esses 8 bytes são o espaço do objeto de classe. Este espaço pode estar na área de pilha ou aplicado dinamicamente na área de heap, dependendo do usuário;
mas char*aponta para um espaço contínuo. O tamanho deste espaço é incerto. Ele é aplicado dinamicamente no construtor quando o objeto da classe é instanciado, o qual não pode ser alterado pelo usuário da classe. Esse espaço aberto dinamicamente é o recurso da classe, que só pode ser aplicado no construtor e liberado no destruidor. É por isso que malloc não pode satisfazer nosso espaço de aplicativo dinâmico para tipos personalizados.

insira a descrição da imagem aqui

Para o novo tipo personalizado, o uso é consistente com o tipo interno:
ao usar new para aplicar a um único objeto, o construtor é chamado uma vez, ao aplicar para num objetos consecutivos, o construtor é chamado num vezes; delete é o mesmo:

int main()
{
    
    
	A* pa1 = new A;
	//动态开辟一个对象,调用默认构造初始化
	A* pa2 = new A(5);
	//动态开辟一个对象,传参给构造函数初始化
	A* pa3 = new A[5];
	//动态开辟一块连续的类对象空间,全部调用默认构造初始化
	A* pa4 = new A[5]{
    
     1,2,3,4,5 };
	//动态开辟一块连续的类对象空间,分别传参初始化

	delete pa1;
	delete pa2;
	//释放单个元素空间用delete
	delete[] pa3;
	delete[] pa4;
	//释放连续空间需使用delete[] !!!

	return 0;
}

Podemos verificar o status do aplicativo dinâmico depurando:
insira a descrição da imagem aqui
insira a descrição da imagem aqui
insira a descrição da imagem aqui
insira a descrição da imagem aqui
um total de 1+1+5+5=12 construtores e 12 destruidores são chamados:
insira a descrição da imagem aqui

O princípio de realização de novo e excluir

tipo embutido

Se você está solicitando um tipo de espaço embutido, new e malloc, delete e free são basicamente semelhantes. No entanto new, deleteo aplicativo e o lançamento são para o espaço de um único elemento, new []e delete[]o aplicativo é para espaço contínuo e , newquando o espaço do aplicativo falha, uma exceção será lançada e mallocNULL será retornado

Deve-se observar que, ao aplicar ou liberar espaço dinamicamente, o C++ está mais inclinado a lançar uma exceção para refletir a causa da falha quando o aplicativo ou a versão falha. Esse comportamento de lançar uma exceção é implementado em duas funções operator new. Também podemos verificar através da desmontagem que quando um novo espaço é criado, a função new do operador é realmente chamada, e quando a exclusão é feita, a função delete do operador é chamada: Claro, ao aplicar e liberar espaço contínuo, a função e é chamado. No entanto, essas duas funções são implementadas chamando multiple e .operator delete

insira a descrição da imagem aqui
insira a descrição da imagem aqui
operator new[]operator delete[]operator newoperator delete

Noções básicas sobre as funções de operador novo e de exclusão do operador

Podemos dar uma breve olhada na função operator newAND operator delete:

new e delete são operadores para os usuários aplicarem e liberarem memória dinâmica. operator new e operator delete são funções globais fornecidas pelo sistema. new chama o operador nova função global na camada inferior para solicitar espaço e delete usa o operador delete global função na camada inferior para liberar espaço .

Na verdade, o espaço do aplicativo operator newtambém é passadomalloc . Se o espaço do aplicativo malloc for bem-sucedido, ele retornará diretamente. Caso contrário, as contramedidas fornecidas pelo usuário para espaço insuficiente serão implementadas. Se o usuário fornecer essa medida, o aplicativo continuará, caso contrário, uma exceção será lançada. operator deleteEm última análise, é freepara liberar espaço .

tipo personalizado

  1. O princípio de new
    Chame operator newa função para solicitar espaço e, em seguida, execute o construtor no espaço aplicado para concluir a construção do objeto.

  2. O princípio de exclusão
    Executa o destruidor no espaço, conclui a limpeza dos recursos no objeto e, em seguida, chama operator deletea função para liberar o espaço do objeto

  3. O princípio de new T[num]
    chama operator new[]a função, na verdade chama a função no operador new[] operator newpara concluir a aplicação de espaços de objetos num e executa o construtor num vezes no espaço aplicado

  4. O princípio de delete[]
    Execute num destruidores no espaço de objeto liberado, conclua a limpeza de recursos em num objetos e, em seguida, chame o operator delete[]espaço de liberação, que na verdade é chamado no operador delete[] operator deletepara liberar o espaço

posicionamento novo

A localização de new pode usar um pedaço de espaço dinâmico que foi aplicado para chamar o construtor para inicializar o objeto de classe

Localizar novo é geralmente usado em conjunto com o pool de memória (o pool de memória é um espaço que foi solicitado antecipadamente. Solicitar uma parte do espaço com antecedência pode evitar a redução na eficiência causada pelo espaço de aplicativo frequente devido à expansão frequente ). O espaço no pool de memória é apenas para aplicação, não para inicialização, podemos obter a inicialização localizando new:

O formato de utilização do posicionamento new é: new (要初始化的空间的指针) 类型;, vale ressaltar que se o tipo de classe a ser inicializado não possuir um construtor padrão, os parâmetros devem ser passados; new (要初始化的空间的指针) 类型(构造函数参数列表);ao
liberar os recursos do objeto classe inicializado pelo posicionamento new, é necessário chame explicitamente o objeto de classe O destruidor para desalocar:

class A
{
    
    
public:
	A(int a = 0)
		:_a(a)
	{
    
    
		_date = new char[_a + 1] {
    
    0};
		cout << "A(int a = 0)" << endl; //构造函数中打印
	}
	~A()
	{
    
    
		delete[] _date;
		_a = 0;
		cout << "~A()" << endl;  //析构函数中打印
	}
private:
	int _a;
	char* _date;
};

int main()
{
    
    
	A* ptr = (A*)malloc(1000);
	//创建1000个字节的空间
	new(ptr)A(5);
	//用上面申请的空间初始化一个A对象
	ptr->~A();
	//释放上面定位new初始化的对象的资源
	free(ptr);
	//释放malloc出的空间
	return 0;
}

O ponto a ser observado é a diferença entre o espaço de classe e o espaço de recurso de classe : aqui, malloc aplica-se apenas a um pedaço de espaço, e a novidade a seguir é inicializar o espaço em malloc. Esse comportamento de inicialização inclui a aplicação de recursos de classe, que não pertencem para malloc fora do espaço;
delete libera os recursos da classe e free perde o espaço de malloc.

A diferença entre new&delete e malloc&free

  1. malloc e free são funções; new e delete são operadores

  2. O espaço solicitado por malloc não será inicializado; new pode ser inicializado

  3. Quando malloc solicita espaço, você precisa calcular manualmente o tamanho do espaço e repassá-lo; novo só precisa ser seguido pelo tipo de espaço. Se houver vários objetos, especifique o número de objetos em [num]

  4. O valor de retorno de malloc void*deve ser forçado quando usado; new não precisa disso, porque o tipo de espaço será especificado após new

  5. Quando malloc falha ao solicitar espaço, ele retorna NULL, portanto, deve ser considerado vazio ao usá-lo; new não precisa dele, mas new precisa capturar exceções

  6. Ao solicitar um tipo de objeto personalizado, malloc/free abrirá apenas espaço e não chamará o construtor e o destruidor; enquanto new chamará o construtor para concluir a inicialização do objeto após solicitar espaço e excluir chamará o destrutor antes de liberar o espaço Conclua a limpeza dos recursos no espaço

Resumir

Neste ponto, a introdução do gerenciamento de memória C/C++ acabou.
Acredito que você não apenas aprendeu o uso de new e delete em C++, mas também aprofundou sua compreensão do gerenciamento de memória.

Se você acha que não apresentei uma determinada parte claramente ou que há um problema com uma determinada parte, sinta-se à vontade para mencioná-lo na área de comentários

Se este artigo for útil para você, espero que seja conectado com um clique

Espero fazer progressos junto com você

Acho que você gosta

Origin blog.csdn.net/weixin_73450183/article/details/131139176
Recomendado
Clasificación