Pergunta

Esta é uma pergunta que está me incomodando há algum tempo. Eu sempre pensei que C ++ deve ter sido concebido de modo que o operador "delete" (sem colchetes) funciona mesmo com o "novo []" operador.

Na minha opinião, escrevendo o seguinte:

int* p = new int;

deve ser equivalente a atribuição de uma matriz de um elemento:

int* p = new int[1];

Se isso fosse verdade, o "delete" operador poderia ser sempre eliminar matrizes, e não seria necessário o "delete []" operador.

Existe alguma razão para que o "delete []" operador foi introduzido em C ++? A única razão que eu posso pensar é que alocar matrizes tem uma pegada pequena da memória (você tem que armazenar a algum lugar tamanho da matriz), de modo que a distinção "delete" vs "delete []" era uma pequena otimização de memória.

Foi útil?

Solução

É para que os destruidores dos elementos individuais será chamado. Sim, para matrizes de PODs, não há muita diferença, mas em C ++, você pode ter matrizes de objetos com destruidores não-triviais.

Agora, a pergunta é, por que não fazer new e delete se comportam como new[] e delete[] e se livrar de new[] e delete[]? Eu voltaria livro de Stroustrup "Projeto e Evolução", onde ele disse que se você não usar os recursos C ++, você não deve ter que pagar por eles (em tempo de execução, pelo menos). A forma como ele está agora, um new ou delete irá se comportar de forma tão eficiente como malloc e free. Se delete tinha o significado delete[], haveria alguma sobrecarga extra em tempo de execução (como James Curran apontado).

Outras dicas

Droga, eu perdi todo o ponto de pergunta, mas vou deixar a minha resposta original como um sidenote. Por que temos de apagar [] é porque há muito tempo que tínhamos de exclusão [CNT], ainda hoje, se você escrever de exclusão [9] ou excluir [CNT], o compilador simplesmente ignora a coisa entre [], mas compila ok. Nessa altura, C ++ foi processado em primeiro lugar por uma frente-fim e, em seguida, alimentada a um compilador C comum. Eles não poderia fazer o truque de armazenar a algum lugar contagem por baixo da cortina, talvez eles não podia sequer pensar nisso naquele momento. E para compatibilidade com versões anteriores, os compiladores provavelmente utilizado o valor dado entre o [] como a contagem da matriz, se não há tal valor, então eles tem a contagem a partir do prefixo, por isso funcionou para os dois lados. Mais tarde, nós digitado nada entre [] e tudo funcionou. Hoje, eu não acho que "delete []" é necessário, mas as implementações exigi-lo dessa forma.

A minha resposta original (que perde o ponto) ::

"Delete" exclui um único objeto. "Delete []" exclui uma matriz de objeto. Para [Apagar] para o trabalho, a implementação mantém o número de elementos na matriz. Eu apenas verifiquei isso, a depuração do código ASM. Na execução (VS2005) I testado, a contagem foi armazenado como um prefixo com a matriz de objeto.

Se você usar "delete []" em um único objeto, a variável de contagem é de lixo para as falhas de código. Se você usar "delete" para uma matriz de objeto, por causa de alguma inconsistência, o código de falha. Eu testei estes casos só agora!

"eliminar apenas apaga a memória alocada para a matriz." declaração em outra resposta não está certo. Se o objeto é uma classe, de exclusão irá chamar o dtor. Basta colocar um ponto de interrupção int o código dtor e excluir o objeto, o ponto de interrupção vai bater.

O que me ocorreu é que, se o compilador e bibliotecas assumido que todos os objetos alocados pelo "novo" são matrizes de objetos, seria OK para chamar "delete" para objetos individuais ou arranjos de objetos. objetos individuais apenas seria o caso especial de uma matriz de objeto ter uma contagem de 1. Talvez haja algo que eu estou em falta, de qualquer maneira ...

Uma vez que toda a gente parece ter perdido o ponto da sua pergunta, vou apenas acrescentar que eu tinha o mesmo pensamento alguns anos atrás, e nunca ter sido capaz de obter uma resposta.

A única coisa que posso pensar é que há muito pouco de sobrecarga extra para tratar um único objeto como uma matriz (um "for(int i=0; i<1; ++i)" desnecessário)

Adicionando isso desde que nenhuma outra resposta atualmente aborda-lo:

matriz delete[] não pode ser usado em uma classe ponteiro-para-base de cada vez - enquanto as lojas do compilador a contagem de objetos Quando você invocar new[], ele não armazena os tipos ou tamanhos dos objetos (como David apontou, em C ++ você raramente pagar por um recurso que você não está usando). No entanto, delete escalar pode excluir com segurança através de classe base, por isso é usado tanto para limpeza de objeto normal e limpeza polimórfica:

struct Base { virtual ~Base(); };
struct Derived : Base { };
int main(){
    Base* b = new Derived;
    delete b; // this is good

    Base* b = new Derived[2];
    delete[] b; // bad! undefined behavior
}

No entanto, no caso oposto - destructor não-virtual - delete escalar deve ser o mais barato possível - não deve verificar se há número de objetos, nem para o tipo de objeto que está sendo excluído. Isso faz com que exclua em um built-in tipo ou tipo old-dados planície muito barato, como a necessidade compilador única invocação ::operator delete e nada mais:

int main(){
    int * p = new int;
    delete p; // cheap operation, no dynamic dispatch, no conditional branching
}

Apesar de não ser um tratamento exaustivo de alocação de memória, espero que este ajuda a esclarecer a amplitude de opções de gerenciamento de memória disponíveis em C ++.

Marshall Cline tem alguma informação sobre este tema .

garante delete [] que o destruidor de cada membro é chamado (se aplicável para o tipo), enquanto delete apenas apaga a memória alocada para a matriz.

Aqui está uma boa leitura: http: //www.informit. com / guias / content.aspx? g = cplusplus & seqnum = 287

E não, tamanhos de matriz não são armazenados em qualquer lugar no C ++. (Obrigado a todos por apontar que esta afirmação é impreciso.)

Estou um pouco confuso com a resposta de Aaron e francamente admitir que eu não entendo completamente por que e onde exclusão [] é necessário.

Eu fiz algumas experiências com o seu código de amostra (após corrigir alguns erros de digitação). Aqui estão os meus resultados. Os erros tipográficos: Base de dados de ~ necessário um corpo função Base de Dados * b foi declarada duas vezes

struct Base { virtual ~Base(){ }>; };
struct Derived : Base { };
int main(){
Base* b = new Derived;
delete b; // this is good

<strike>Base</strike> b = new Derived[2];
delete[] b; // bad! undefined behavior
}

A compilação e execução

david@Godel:g++ -o atest atest.cpp 
david@Godel: ./atest 
david@Godel: # No error message

programa modificado com delete [] removido

struct Base { virtual ~Base(){}; };
struct Derived : Base { };

int main(){
    Base* b = new Derived;
    delete b; // this is good

    b = new Derived[2];
    delete b; // bad! undefined behavior
}

A compilação e execução

david@Godel:g++ -o atest atest.cpp 
david@Godel: ./atest 
atest(30746) malloc: *** error for object 0x1099008c8: pointer being freed was n
ot allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

É claro, eu não sei se apagar [] b está realmente trabalhando no primeiro exemplo; Eu só sei que não dá uma mensagem de erro do compilador.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top