É 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 é newly-created.

Estou a pensar numa coisa destas.
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?

Author: Rakete1111, 2010-06-30

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).
 207
Author: JaredPar, 2015-05-20 22:34:02

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.

Para qualquer pessoa que se importe com a fiabilidade deste tipo de codificação, Se fizer uma chamada para, de, ou através quase em qualquer parte da Europa, há uma boa chance de que ele está sendo tratado (pelo menos em parte) por um código que faz exatamente isso.
 72
Author: Jerry Coffin, 2018-04-01 00:56:43
Se isso te assusta, há uma invasão perfeitamente legal.
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.

 42
Author: Mark Ransom, 2015-10-15 18:20:18

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.

 21
Author: Bob Bryan, 2012-06-29 21:41:25

É 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; }.

 19
Author: Kirill V. Lyadvinsky, 2010-06-30 16:19:23

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;
}
 13
Author: UnknownGosu, 2010-06-30 16:30:05
Podes fazê-lo. No entanto, não pode atribuir isto. Assim, a razão pela qual você afirma para fazer isso, "eu quero mudar a visão", parece muito questionável. O melhor método, em minha opinião, seria para o objeto que tem a visão para substituir essa visão. É claro que estás a usar objectos RAII e por isso não precisas de ligar para o delete...certo?
 7
Author: Crazy Eddie, 2010-06-30 16:19:08

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.

 5
Author: Zack Yezek, 2013-12-20 05:04:57
Esta é uma pergunta antiga, respondida, mas @Alexandre perguntou: "Por que alguém faria isso?", e eu pensei que eu poderia fornecer um uso exemplo que estou considerando esta tarde. Código legado. Usa ponteiros nus Obj * obj com um obj delete no final. Infelizmente, às vezes, não muitas vezes, preciso de manter o objecto vivo por mais tempo. Estou a considerar fazer dele um indicador inteligente de referência. Mas haveria lotes de código para mudar, se Eu devia usar 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.)

 3
Author: Krazy Glew, 2012-06-24 13:09:01

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

 0
Author: Swift - Friday Pie, 2016-10-04 06:24:50