Esta é uma pergunta interessante!
Desde que Deletealtera o comprimento da matriz dinâmica - assim como SetLengthfaz - ele precisa realocar a matriz dinâmica. E também altera o ponteiro dado a esse novo local na memória. Mas, obviamente, ele não pode alterar nenhum outro ponteiro para o antigo array dinâmico.
Portanto, ele deve diminuir a contagem de referência da antiga matriz dinâmica e criar uma nova matriz dinâmica com uma contagem de referência de 1. O ponteiro fornecido Deleteserá definido para essa nova matriz dinâmica.
Portanto, o antigo array dinâmico deve ser intocado (exceto pela contagem reduzida de referências, é claro). Isso está essencialmente documentado para a SetLengthfunção semelhante :
Após uma chamada para SetLength, Sé garantido que faça referência a uma string ou matriz exclusiva - ou seja, uma string ou matriz com uma contagem de referência de uma.
Mas, surpreendentemente, isso não acontece exatamente neste caso.
Considere este exemplo mínimo:
procedure TForm1.FormCreate(Sender: TObject);
var
a, b: array of Integer;
begin
a := [$AAAAAAAA, $BBBBBBBB]; {1}
b := a; {2}
Delete(a, 0, 1); {3}
end;
Eu escolhi os valores para que sejam fáceis de localizar na memória (Alt + Ctrl + E).
Após (1), aaponte para $02A2C198minha execução de teste:
02A2C190 02 00 00 00 02 00 00 00
02A2C198 AA AA AA AA BB BB BB BB
Aqui, a contagem de referência é 2 e o comprimento da matriz é 2, conforme o esperado. (Consulte a documentação para o formato de dados interno para matrizes dinâmicas.)
Depois de (2) a = b, isto é Pointer(a) = Pointer(b),. Ambos apontam para a mesma matriz dinâmica, que agora se parece com isso:
02A2C190 03 00 00 00 02 00 00 00
02A2C198 AA AA AA AA BB BB BB BB
Como esperado, a contagem de referência agora é 3.
Agora, vamos ver o que acontece depois (3). aagora aponta para uma nova matriz dinâmica 2A30F88no meu teste:
02A30F80 01 00 00 00 01 00 00 00
02A30F88 BB BB BB BB 01 00 00 00
Como esperado, esse novo array dinâmico possui uma contagem de referência de 1 e apenas o "elemento B".
Eu esperaria que a antiga matriz dinâmica, que bainda está apontando, parecesse como antes, mas com uma contagem de referência reduzida de 2. Mas parece com isso agora:
02A2C190 02 00 00 00 02 00 00 00
02A2C198 BB BB BB BB BB BB BB BB
Embora a contagem de referência seja realmente reduzida para 2, o primeiro elemento foi alterado.
Minha conclusão é que
(1) Faz parte do contrato do Deleteprocedimento que ele invalida todas as outras referências à matriz dinâmica inicial.
ou
(2) Deve se comportar como descrevi acima, caso em que isso é um bug.
Infelizmente, a documentação para o Deleteprocedimento não menciona isso.
Parece um bug.
Atualização: o código RTL
Eu dei uma olhada no código fonte do Deleteprocedimento, e isso é bastante interessante.
Pode ser útil comparar o comportamento com o de SetLength(porque esse funciona corretamente):
Se a contagem de referência da matriz dinâmica for 1, SetLengthtente simplesmente redimensionar o objeto de heap (e atualizar o campo de comprimento da matriz dinâmica).
Caso contrário, SetLengthcria uma nova alocação de heap para uma nova matriz dinâmica com uma contagem de referência de 1. A contagem de referência da matriz antiga é reduzida em 1.
Dessa forma, é garantido que a contagem de referência final seja sempre 1- ou foi desde o início ou uma nova matriz foi criada. (É bom que você nem sempre faça uma nova alocação de heap. Por exemplo, se você tiver uma matriz grande com uma contagem de referência 1, simplesmente truncá-la é mais barato do que copiá-la para um novo local.)
Agora, como Deletesempre reduz a matriz, é tentador tentar simplesmente reduzir o tamanho do objeto de heap onde está. E é realmente isso que o código RTL tenta System._DynArrayDelete. Portanto, no seu caso, o BBBBBBBBé movido para o início da matriz. Tudo está bem.
Mas então chama System.DynArraySetLength, que também é usado por SetLength. E este procedimento contém o seguinte comentário,
// If the heap object isn't shared (ref count = 1), just resize it. Otherwise, we make a copy
antes de detectar que o objeto é realmente compartilhado (no nosso caso, ref count = 3), faz uma nova alocação de heap para uma nova matriz dinâmica e copia a antiga (reduzida) para esse novo local. Reduz a contagem de ref da matriz antiga e atualiza a contagem de ref, o comprimento e o ponteiro de argumento da nova.
Então, acabamos com uma nova matriz dinâmica de qualquer maneira. Mas os programadores RTL esqueceu que já tinha foi cancelada a matriz original, que agora consiste na nova matriz colocada em cima do antigo: BBBBBBBB BBBBBBBB.