É permitido apagar isto?
é permitido delete this;
se a declaração de supressão for a última declaração que será executada nessa instância da classe? Claro que tenho certeza que o objeto representado pelo this
-ponteiro é new
ly-created.
void SomeModule::doStuff()
{
// in the controller, "this" object of SomeModule is the "current module"
// now, if I want to switch over to a new Module, eg:
controller->setWorkingModule(new OtherModule());
// since the new "OtherModule" object will take the lead,
// I want to get rid of this "SomeModule" object:
delete this;
}
Posso fazer isto?
10 answers
A FAQ de C++ tem um item específico para este
Acho que esta citação resume tudo muito bem.Desde que tenhas cuidado, não faz mal que um objecto se suicide (apaga isto).
Sim, {[0] } definiu resultados, desde que (como você observou) você garanta que o objeto foi alocado dinamicamente, e (é claro) nunca tentar usar o objeto depois que ele é destruído. Ao longo dos anos, muitas perguntas foram feitas sobre o que o padrão diz especificamente sobre {[[0]}, em oposição a excluir algum outro ponteiro. A resposta a isso é bastante curta e simples: não diz muito de nada. Apenas diz que o operando de delete
deve ser uma expressão que designa um ponteiro para um objeto, ou um conjunto de objetos. Ele entra em um grande detalhe sobre coisas como como descobrir o que (se houver) função de desallocação chamar para liberar a memória, mas toda a seção em delete
(§[expr.delete]) não menciona {[[0]} especificamente de todo. A secção sobre destruidores menciona delete this
num só lugar (Classe.dtor]/13):
No ponto de definição de destruidor virtual (incluindo uma definição implícita (15.8)), a desallocação não-array a função é determinada como se, para a expressão, esta aparecesse num destruidor não virtual da classe do destruidor (ver 8.3.5).
Isso tende a suportar a ideia de que o padrão considera {[[0]} válido--se fosse inválido, o seu tipo não teria significado. É o único lugar que o standard menciona, tanto quanto sei.
De qualquer forma, alguns consideram uma invasão desagradável e dizem a quem quiser ouvir que deve ser evitada. Um comumente citado o problema é a dificuldade de garantir que os objetos da classe só são alocados dinamicamente. Outros consideram-no um idioma perfeitamente razoável, e usá-lo o tempo todo. Pessoalmente, estou em algum lugar no meio: raramente o uso, mas não hesite em fazê-lo quando parece ser a ferramenta certa para o trabalho.Edite: [principalmente em resposta ao Comentário de @Alexandre C]: a primeira vez que você faz isso é com um objeto que tem uma vida que é quase inteiramente sua. Um exemplo que James Kanze tem cited era um sistema de facturação / rastreamento que ele trabalhava para uma companhia de telefones. Basicamente, quando você pega o telefone, algo toma nota disso e cria um objeto phone_call
. A partir desse ponto em diante, o objeto phone_call
lida com os detalhes da chamada telefônica (fazendo uma conexão quando você discar, adicionando uma entrada na base de dados para dizer quando a chamada começou, possivelmente conectar mais pessoas se você fizer uma chamada de conferência, etc.) Quando você desliga, o objeto phone_call
faz a sua contabilidade final (por exemplo, adiciona uma entrada ao banco de dados para dizer quando você desligou, para que eles possam calcular quanto tempo sua chamada foi) e então se destrói a si mesmo. A vida útil do objeto phone_call
é baseada em quando você pega/desliga o telefone -- do ponto de vista do resto do sistema, é basicamente totalmente arbitrário, então você não pode ligá-lo a qualquer âmbito lexical no código, ou qualquer coisa nessa ordem.
void myclass::delete_me()
{
std::unique_ptr<myclass> bye_bye(this);
}
Eu acho que {[[2]} é idiomático C++, e eu só apresento isto como uma curiosidade.
Existe um caso em que esta construção é realmente útil - você pode apagar o objecto depois de lançar uma excepção que necessita de dados de membros do objecto. O objeto permanece válido até o lançamento ocorrer.
void myclass::throw_error()
{
std::unique_ptr<myclass> bye_bye(this);
throw std::runtime_exception(this->error_msg);
}
Nota: Se estiver a usar um compilador mais antigo que o C++11, poderá usar std::auto_ptr
em vez de std::unique_ptr
, fará a mesma coisa.
Uma das razões para o C++ ter sido concebido foi para facilitar a reutilização do Código. Em geral, C++ deve ser escrito para que funcione se a classe está instanciada no heap, em um array, ou na pilha. "Delete this" é uma prática de codificação muito ruim, porque ele só vai funcionar se uma única instância é definida no heap; e é melhor não haver outra declaração de delete, que é tipicamente usado pela maioria dos desenvolvedores para limpar o heap. Ao fazê-lo, parte-se também do princípio de que nenhuma manutenção programador no futuro vai curar uma perda de memória falsamente percebida, adicionando uma declaração de delete.
Mesmo que saiba antecipadamente que o seu plano actual é apenas atribuir uma única instância no heap, e se algum desenvolvedor feliz-go-lucky aparecer no futuro e decidir criar uma instância na pilha? Ou, e se ele corta e passa certas partes da classe para uma nova classe que pretende usar na pilha? Quando o código chega a "apagar isto" ele vai desligar e apagar mas quando o objecto sair do alcance, vai chamar o destruidor. O destruidor vai tentar apagá-lo novamente e depois você é tramado. No passado, fazer algo assim estragaria não só o programa, mas o sistema operacional e o computador precisariam ser reiniciados. Em todo o caso, isto não é altamente recomendado e deve quase sempre ser evitado. Eu teria que estar desesperado, seriamente bêbado, ou realmente odiar a empresa para a qual trabalhei para escrever um código que fez presente.
É permitido (apenas não use o objeto depois disso), mas eu não escreveria tal código na prática. Eu acho que {[[0]} deve aparecer apenas em funções que se chamam release
ou Release
e se parecem com: void release() { ref--; if (ref<1) delete this; }
.
Bem, no Component Object Model (com) delete this
a construção pode fazer parte do método {[[2]} que é chamado sempre que se quer libertar um objecto aquisited:
void IMyInterface::Release()
{
--instanceCount;
if(instanceCount == 0)
delete this;
}
Este é o idioma principal para objetos contados de referência.
A contagem de referências é uma forma forte de recolha de lixo determinística - garante que os objectos gerem a sua própria vida em vez de dependerem de ponteiros inteligentes, etc. para o fazer por eles. O objeto subjacente é acessado apenas através de ponteiros inteligentes de "referência", projetados para que os ponteiros incrementem e decremam um inteiro membro (a contagem de referência) no objeto real.
Quando a última referência cair da pilha ou é excluído, a contagem de referência vai para zero. O comportamento padrão do seu objeto será então uma chamada para " apagar isso "para coletar lixo - as bibliotecas que eu escrevo fornecem uma chamada virtual protegida" CountIsZero " na classe base para que você possa anular esse comportamento para coisas como caching.
A chave para tornar isto seguro não é permitir aos utilizadores o acesso ao construtor do objecto em questão (torná-lo protegido), mas em vez disso fazê-los chamar algum membro estático-o tipo de fábrica "static Reference CreateT (...)". Dessa forma, você sabe com certeza que eles são sempre construídos com "novo" comum e que nenhum ponteiro raw nunca está disponível, então "excluir isto" nunca vai explodir.
ref_cnt_ptr<Obj>
em todo o lado. E se você misturar Obj * nu e ref_cnt_ptr, você pode obter o objeto implicitamente excluído quando o último ref_cnt_ptr desaparece, mesmo que haja Obj * ainda vivo.
Então estou a pensar em criar um explicit_delete_ref_cnt_ptr. Ou seja, um ponteiro contado de referência em que a supressão só é efectuada numa rotina explícita de eliminação. Usando-o no único lugar onde o código existente conhece a vida útil do objeto, bem como no meu novo código que mantém o objeto vivo há mais tempo.
Aumentar e diminuir a contagem de referência como explicit_delete_ref_cnt_ptr obter manipulado.
Mas não liberta quando a contagem de referência é vista como zero no explicit_delete_ref_cnt_ptr destructor.
Apenas libertando quando a contagem de referência é vista como zero numa operação explícita de eliminação. Por exemplo, em algo como:
template<typename T> class explicit_delete_ref_cnt_ptr {
private:
T* ptr;
int rc;
...
public:
void delete_if_rc0() {
if( this->ptr ) {
this->rc--;
if( this->rc == 0 ) {
delete this->ptr;
}
this->ptr = 0;
}
}
};
Está bem, algo do género. É um pouco incomum ter um tipo de ponteiro de referência contado Não automaticamente apaga o objecto apontado no destruidor de ptr rc'ed. Mas parece que isso pode tornar a mistura de ponteiros nus e ponteiros rc'Ed um pouco mais seguro.
Mas até agora não há necessidade de apagar isto.
Mas então ocorreu-me: se o objecto apontado, o ponteiro, sabe que está a ser contado de referência, por exemplo se a contagem está dentro do objecto( ou em alguma outra tabela), então a rotina delete_if_rc0 pode ser um método do objecto pointee, não o (inteligente) ponteiro.
class Pointee {
private:
int rc;
...
public:
void delete_if_rc0() {
this->rc--;
if( this->rc == 0 ) {
delete this;
}
}
}
};
Na verdade, não precisa de ser um método membro, mas pode ser uma função livre:
map<void*,int> keepalive_map;
template<typename T>
void delete_if_rc0(T*ptr) {
void* tptr = (void*)ptr;
if( keepalive_map[tptr] == 1 ) {
delete ptr;
}
};
(BTW, eu sei que o código não está bem - torna-se menos legível Se eu adicionar todos os detalhes, então eu vou deixá-lo assim.)
Apague isto é legal desde que o objecto esteja em heap. Você precisaria exigir que o objeto fosse apenas heap. A única maneira de fazer isso é tornar o destruidor protegido - desta forma Excluir pode ser chamado apenas a partir da classe, para que você precisasse de um método que garantisse a exclusão