Gerenciamento de memória C/C++
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 new
espaç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:
Deve-se observar que new
e 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 int
e 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.
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:
um total de 1+1+5+5=12 construtores e 12 destruidores são chamados:
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
, delete
o 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 , new
quando o espaço do aplicativo falha, uma exceção será lançada e malloc
NULL 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
operator new[]
operator delete[]
operator new
operator 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 new
AND 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 new
també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 delete
Em última análise, é free
para liberar espaço .
tipo personalizado
-
O princípio de new
Chameoperator new
a função para solicitar espaço e, em seguida, execute o construtor no espaço aplicado para concluir a construção do objeto. -
O princípio de exclusão
Executa o destruidor no espaço, conclui a limpeza dos recursos no objeto e, em seguida, chamaoperator delete
a função para liberar o espaço do objeto -
O princípio de new T[num]
chamaoperator new[]
a função, na verdade chama a função no operador new[]operator new
para concluir a aplicação de espaços de objetos num e executa o construtor num vezes no espaço aplicado -
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 ooperator delete[]
espaço de liberação, que na verdade é chamado no operador delete[]operator delete
para 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
-
malloc e free são funções; new e delete são operadores
-
O espaço solicitado por malloc não será inicializado; new pode ser inicializado
-
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]
-
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 -
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
-
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ê