É seguro excluir um ponteiro NULL?

299

É seguro excluir um ponteiro NULL?

E é um bom estilo de codificação?

qiuxiafei
fonte
21
A boa prática é escrever programas C ++ sem uma única chamada para delete. Use RAII em vez disso. Ou seja, use em std::vector<T> v(100);vez de T* p = new T[100];usar ponteiros inteligentes como unique_ptr<T>e shared_ptr<T>que cuidam da exclusão em vez de ponteiros brutos etc.
fredoverflow
8
graças a make_shared(c ++ 11) e make_unique(c ++ 14) o programa deve conter de zero de newedelete
sp2danny
2
Ainda pode haver alguns casos raros que exigem nova / exclusão, por exemplo, atômico <T *>: atômico <p__trico <T>> não é permitido e <atômico_ptr <T>> atômico possui sobrecarga que pode ser inaceitável em alguns casos.
atb
2
Para declarar uma classe com gerenciamento de recursos usando RAII, você precisa chamar new e delete right?
VinGarcia #
2
@VinGarcia O ponto é que a maioria dos códigos de usuário / cliente (ou seja: sem biblioteca) nunca deve escrever newou delete. As classes projetadas para gerenciar recursos, onde os componentes Standard não podem fazer o trabalho, podem, é claro, fazer o que precisam, mas o ponto é que eles fazem coisas feias com a memória que gerenciam, não com o código do usuário final. Portanto, crie sua própria classe library / helper para fazer new/ deletee use essa classe em vez delas.
Underscore_d

Respostas:

265

deleteexecuta a verificação de qualquer maneira; portanto, verificá-la do seu lado aumenta a sobrecarga e fica mais feia. Uma prática muito boa é definir o ponteiro para NULL depois delete(ajuda a evitar a exclusão dupla e outros problemas semelhantes de corrupção de memória).

Eu também adoraria se, deletepor padrão, estava definindo o parâmetro como NULL, como em

#define my_delete(x) {delete x; x = NULL;}

(Eu sei sobre os valores de R e L, mas não seria bom?)

ruslik
fonte
72
Observe que ainda pode haver vários outros ponteiros apontando para o mesmo objeto, mesmo se você definir um para NULL na exclusão.
sth
13
Na maioria dos casos, no meu código, o ponteiro fica fora do escopo depois de excluído. Muito mais seguro do que simplesmente defini-lo como NULL.
jalf
142
Uma prática muito boa não é definir o ponteiro como NULL após a exclusão. Definir um ponteiro como NULL após excluí-lo mascara erros de alocação de memória, o que é uma coisa muito ruim. Um programa que está correto não exclui um ponteiro duas vezes e um programa que exclui um ponteiro duas vezes deve falhar.
Damon
15
@ Alice: É irrelevante o que o padrão diz a esse respeito. O padrão definido excluindo um ponteiro nulo é válido por algum motivo absurdo 30 anos atrás, por isso é legal (provavelmente um legado em C). Mas excluir o mesmo ponteiro duas vezes (mesmo depois de alterar seu padrão de bits) ainda é um erro grave do programa. Não pela redação do padrão, mas pela lógica e propriedade do programa. Assim como a exclusão de um ponteiro nulo, como o ponteiro nulo não corresponde a nenhum objeto , nada pode ser excluído. Um programa deve saber exatamente se um objeto é válido e quem o possui e quando pode ser excluído.
Damon
27
@ Damon No entanto, apesar dessas revogações de suas regras de propriedade draconianas, as estruturas sem bloqueio são comprovadamente mais robustas do que as baseadas em bloqueio. E sim, meus colegas de trabalho realmente me amam pelo perfil de execução aprimorado que essas estruturas fornecem e pela segurança rigorosa de threads que mantêm, o que permite raciocinar mais facilmente sobre o código (ótimo para manutenção). No entanto, nada disso nem seu ataque pessoal implícito têm a ver com qualquer definição de correção, validade ou propriedade. O que você propõe é uma boa regra de ouro, mas não é uma lei universal nem consagrada no padrão.
18171 Alice
77

Do rascunho C ++ 0x Standard.

$ 5.3.5 / 2 - "[...] Em qualquer uma das alternativas, o valor do operando de exclusão pode ser um valor de ponteiro nulo. [...]"

Obviamente, ninguém faria 'delete' de um ponteiro com valor NULL, mas é seguro fazê-lo. Idealmente, não se deve ter código que exclua um ponteiro NULL. Mas às vezes é útil quando a exclusão de ponteiros (por exemplo, em um contêiner) acontece em um loop. Como a exclusão de um valor de ponteiro NULL é segura, pode-se realmente escrever a lógica de exclusão sem verificações explícitas para que o operando NULL seja excluído.

Como um aparte, o C Standard $ 7.20.3.2 também diz que 'free' em um ponteiro NULL não faz nenhuma ação.

A função livre faz com que o espaço apontado por ptr seja desalocado, isto é, disponibilizado para alocação adicional. Se ptr for um ponteiro nulo, nenhuma ação ocorrerá.

Chubsdad
fonte
2
Eu realmente gostaria desta resposta por suas citações se ela não introduzisse intencionalmente ineficiência no código não otimizado. Como afirma a resposta aceita, a exclusão de um ponteiro nulo é um não-op. Portanto, verificar se um ponteiro é nulo antes de excluí-lo é completamente estranho.
Codetaku 24/03
47

Sim, é seguro.

Não há mal em excluir um ponteiro nulo; muitas vezes reduz o número de testes no final de uma função se os ponteiros não alocados forem inicializados em zero e simplesmente excluídos.


Como a frase anterior causou confusão, um exemplo - que não é seguro para exceções - do que está sendo descrito:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    
    pst = new SomeType;
    
    if (…)
    {
        pat = new AnotherType[10];
        
    }
    if (…)
    {
        code using pat sometimes
    }

    delete[] pat;
    delete pst;
}

Existem todos os tipos de lêndeas que podem ser selecionadas com o código de exemplo, mas o conceito (espero) é claro. As variáveis ​​do ponteiro são inicializadas em zero, para que as deleteoperações no final da função não precisem testar se são não nulas no código-fonte; o código da biblioteca executa essa verificação de qualquer maneira.

Jonathan Leffler
fonte
Eu tive que ler isso algumas vezes para entender isso. Você deve significar inicializá-los para zero no topo do método, ou durante ele, não no final, com certeza? Caso contrário, você apenas removeria o zeramento e a exclusão.
Marquês de Lorne
1
@EJP: Um esboço não totalmente implausível de uma função pode ser: void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }. Eu não mostrei nenhuma estrutura de bloco ou saltos, mas as delete[]operações no final são seguras devido às inicializações no início. Se algo que saltou para o final após x1foi alocado e antes y1foi alocado e não houve inicialização y1, haveria um comportamento indefinido - e, embora o código possa testar a nulidade (de x1e y1) antes das exclusões, não há necessidade de fazer tão.
Jonathan Leffler
22

A exclusão de um ponteiro nulo não tem efeito. Não é um bom estilo de codificação necessariamente porque não é necessário, mas também não é ruim.

Se você estiver procurando por boas práticas de codificação, considere o uso de ponteiros inteligentes, então você não precisa delete.

Brian R. Bondy
fonte
20
o momento em que as pessoas desejam excluir um ponteiro NULL é quando não têm certeza se ele contém NULL ... se soubessem que era NULL, não estariam pensando em excluir e, portanto, perguntando ;-).
Tony Delroy
@ Tony: Meu argumento era apenas que isso não terá efeito, e a presença de um código que exclua um ponteiro que às vezes contém NULL não é necessariamente ruim.
Brian R. Bondy
3
As verificações redundantes da IMO certamente são ruins para desempenho, legibilidade e manutenção.
paulm
O @paulm OP certamente não está falando desse tipo de coisa ruim, mais do tipo Seg Fault / UB.
Inverno
8

Para complementar a resposta de ruslik , no C ++ 14 você pode usar esta construção:

delete std::exchange(heapObject, nullptr);
ashrasmun
fonte
3

É seguro, a menos que você tenha sobrecarregado o operador de exclusão. se você sobrecarregou o operador de exclusão e não manipulou a condição nula, ele não é seguro.


fonte
Você poderia adicionar alguma explicação para sua resposta?
Marcin Nabiałek
-2

Eu tenho experimentado que é não segura (VS2010) para excluir [] NULL (ie variedade de sintaxe). Não tenho certeza se isso está de acordo com o padrão C ++.

Ele é seguro para NULL delete (sintaxe escalar).

pest
fonte
6
Isso é ilegal e eu não acredito nisso.
precisa saber é o seguinte
5
Você deve poder excluir qualquer ponteiro nulo. Portanto, se está quebrando para você, provavelmente você tem um erro no código que o mostra.
Mysticial
3
§5.3.2 Na segunda alternativa (matriz de exclusão), o valor do operando de exclusão pode ser um valor de ponteiro nulo ou um valor de ponteiro que resultou de uma nova expressão de matriz anterior.
Sp2danny
1
@Opux O comportamento do VS2010 foi alegado na resposta. Como explicado em outros comentários, é seguro delete[] NULL.
Konrad Rudolph
1
@Opux Foi por isso que escrevi “Não acredito” em vez de “Está errado”. Mas ainda não o fiz, e seria uma violação estupenda e ultrajante do padrão. O VC ++ é geralmente muito bom em seguir as restrições do padrão, e os locais onde os viola fazem sentido historicamente.
Konrad Rudolph