Foo* set = new Foo[100];
// ...
delete [] set;
Você não passa os limites da matriz para delete[]
. Mas onde essas informações são armazenadas? É padronizado?
Foo* set = new Foo[100];
// ...
delete [] set;
Você não passa os limites da matriz para delete[]
. Mas onde essas informações são armazenadas? É padronizado?
Respostas:
Quando você aloca memória no heap, seu alocador acompanhará quanta memória você alocou. Isso geralmente é armazenado em um segmento "head" logo antes da memória que você recebe. Dessa forma, quando é hora de liberar a memória, o desalocador sabe exatamente quanta memória deve ser liberada.
free
sabe quanta memória desalocar". Sim, o tamanho do bloco de memória é armazenado "em algum lugar" por malloc
(normalmente no próprio bloco), e é assim que se free
sabe. No entanto, new[]
/ delete[]
é uma história diferente. Este último trabalha basicamente em cima de malloc
/ free
. new[]
também armazena o número de elementos criados no bloco de memória (independentemente de malloc
), para que mais tarde delete[]
possa recuperar e usar esse número para chamar o número adequado de destruidores.
malloc
) e contagem de elementos (por new[]
). Observe que o primeiro não pode ser usado para calcular o último, pois, em geral, o tamanho do bloco de memória pode ser maior do que o realmente necessário para a matriz do tamanho solicitado. Observe também que o contador do elemento da matriz é necessário apenas para tipos com destruidor não trivial. Para tipos com destruidor trivial, o contador não é armazenado new[]
e, é claro, não é recuperado por delete[]
.
Uma das abordagens para compiladores é alocar um pouco mais de memória e armazenar uma contagem de elementos em um elemento principal.
Exemplo de como isso pode ser feito:
Aqui
int* i = new int[4];
O compilador alocará sizeof(int)*5
bytes.
int *temp = malloc(sizeof(int)*5)
Armazenará "4" nos primeiros sizeof(int)
bytes
*temp = 4;
E definir i
i = temp + 1;
O i
mesmo aponta para uma matriz de 4 elementos, não 5.
E exclusão
delete[] i;
será processado da seguinte maneira:
int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)
A informação não é padronizada. No entanto, nas plataformas em que trabalhei com essas informações, elas são armazenadas na memória antes do primeiro elemento. Portanto, você poderia acessá-lo e inspecioná-lo, no entanto, não vale a pena.
Também é por isso que você deve usar delete [] ao alocar memória com o novo [], pois a versão do array de delete sabe que (e onde) precisa procurar liberar a quantidade certa de memória - e chamar o número apropriado de destruidores para os objetos.
Basicamente, está organizado na memória como:
[info] [mem você pediu ...]
Onde info é a estrutura usada pelo seu compilador para armazenar a quantidade de memória alocada e quais não.
Isso depende da implementação.
É definido no padrão C ++ como específico do compilador. O que significa mágica do compilador. Ele pode romper com restrições de alinhamento não triviais em pelo menos uma plataforma principal.
Você pode pensar em possíveis implementações percebendo que isso delete[]
é definido apenas para ponteiros retornados por new[]
, que podem não ser o mesmo ponteiro retornado por operator new[]
. Uma implementação na natureza é armazenar a contagem de matrizes no primeiro int retornado por operator new[]
e new[]
retornar um deslocamento de ponteiro além disso. (É por isso que os alinhamentos não triviais podem quebrar new[]
.)
Tenha em mente que operator new[]/operator delete[]
! = new[]/delete[]
.
Além disso, isso é ortogonal à forma como C sabe o tamanho da memória alocada por malloc
.
Como a matriz a ser 'excluída' deveria ter sido criada com um único uso do operador 'novo'. A operação 'nova' deveria ter colocado essas informações na pilha. Caso contrário, como os usos adicionais de novos saberão onde o heap termina?
Não é padronizado. No tempo de execução da Microsoft, o novo operador usa malloc () e o operador de exclusão usa free (). Portanto, nessa configuração, sua pergunta é equivalente ao seguinte: Como free () sabe o tamanho do bloco?
Há alguma contabilidade acontecendo nos bastidores, ou seja, no tempo de execução C.
Esse é um problema mais interessante do que você imagina a princípio. Esta resposta é sobre uma possível implementação.
Em primeiro lugar, enquanto em algum nível seu sistema precisa saber como "liberar" o bloco de memória, o malloc / free subjacente (que new / delete / new [] / delete [] geralmente chamam) nem sempre se lembra exatamente da quantidade de memória Se você solicitar, ele poderá ser arredondado (por exemplo, quando estiver acima de 4K, ele é arredondado para o próximo bloco de tamanho 4K).
Portanto, mesmo que possa obter o tamanho do bloco de memória, isso não nos diz quantos valores existem na nova memória [] ed, pois ela pode ser menor. Portanto, precisamos armazenar um número inteiro extra nos dizendo quantos valores existem.
EXCETO, se o tipo que está sendo construído não possui um destruidor, delete [] não precisa fazer nada além de liberar o bloco de memória e, portanto, não precisa armazenar nada!