É permitido delete this;
se a instrução delete for a última instrução que será executada nessa instância da classe? É claro que tenho certeza de que o objeto representado pelo this
ponteiro é new
criado.
Estou pensando em algo assim:
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 isso?
c++
memory-management
new-operator
delete-operator
self-destruction
Martijn Courteaux
fonte
fonte
delete this
criou um forte acoplamento entre a classe e o método de alocação usado para criar objetos dessa classe. Esse é um projeto de OO muito ruim, pois a coisa mais fundamental no OOP é criar classes autônomas que não sabem ou se importam com o que o chamador está fazendo. Portanto, uma classe projetada corretamente não deve saber ou se importar com como foi alocada. Se, por algum motivo, você precisar de um mecanismo tão peculiar, acho que um design melhor seria usar uma classe de wrapper em torno da classe real e deixar o wrapper lidar com a alocação.setWorkingModule
?Respostas:
O C ++ FAQ Lite possui uma entrada específica para este
Eu acho que essa citação resume bem
fonte
Sim,
delete this;
definiu resultados, desde que (como você notou) assegure que o objeto foi alocado dinamicamente e (é claro) nunca tente usar o objeto depois que ele for destruído. Ao longo dos anos, muitas perguntas foram feitas sobre o que o padrão diz especificamentedelete this;
, em vez de excluir outro indicador. A resposta para isso é bastante curta e simples: não diz muito de nada. Apenas diz quedelete
o operando deve ser uma expressão que designa um ponteiro para um objeto ou uma matriz de objetos. Ele entra em detalhes sobre coisas como descobrir qual (se houver) função de desalocação chamar para liberar a memória, mas a seção inteira emdelete
(§ [expr.delete]) não mencionadelete this;
especificamente nada. A seção sobre destruidores mencionadelete this
em um só lugar (§ [class.dtor] / 13):Isso tende a apoiar a ideia de que o padrão considera
delete this;
válido - se fosse inválido, seu tipo não seria significativo. Esse é o único lugar que o padrão mencionadelete this;
, até onde eu sei.De qualquer forma, alguns consideram
delete this
um truque desagradável e dizem a quem quiser ouvir que deve ser evitado. Um problema comumente citado é a dificuldade de garantir que os objetos da classe só sejam alocados dinamicamente. Outros o consideram um idioma perfeitamente razoável e o usam o tempo todo. Pessoalmente, estou em algum lugar no meio: raramente o uso, mas não hesite em fazê-lo quando parecer a ferramenta certa para o trabalho.A primeira vez que você usa essa técnica é com um objeto que tem uma vida quase inteiramente própria. Um exemplo que James Kanze citou foi um sistema de cobrança / rastreamento no qual ele trabalhou para uma companhia telefônica. Quando você inicia uma ligação, algo toma nota disso e cria um
phone_call
objeto. A partir desse momento, ophone_call
objeto lida com os detalhes da chamada telefônica (estabelecendo uma conexão quando você disca, adicionando uma entrada ao banco de dados para dizer quando a chamada foi iniciada, possivelmente conecte mais pessoas se você fizer uma chamada em conferência, etc.) Quando as últimas pessoas na ligação desligam, ophone_call
objeto faz a contabilidade final (por exemplo, adiciona uma entrada ao banco de dados para dizer quando você desligou, para que eles possam calcular quanto tempo durou a ligação) e depois se destrói. A vida útil dophone_call
O objeto é baseado no momento em que a primeira pessoa inicia a chamada e quando as últimas saem - do ponto de vista do resto do sistema, é basicamente totalmente arbitrário, portanto você não pode vinculá-lo a nenhum escopo lexical no código , ou qualquer coisa nessa ordem.Para qualquer pessoa que se preocupe com a confiabilidade desse tipo de codificação: se você ligar para, de ou através de quase qualquer parte da Europa, há uma boa chance de que ele seja tratado (pelo menos em parte) pelo código isso faz exatamente isso.
fonte
bool selfDelete
parâmetro no construtor que é atribuído a uma variável de membro. É verdade que isso significa entregar ao programador corda suficiente para amarrar um laço nele, mas acho isso preferível a vazamentos de memória.this
. Sim, o código está sendo tratado exatamentethis
. ;)Se isso te assusta, há um truque perfeitamente legal:
Eu acho que
delete this
é C ++ idiomático, e só apresento isso como uma curiosidade.Há um caso em que essa construção é realmente útil - você pode excluir o objeto depois de lançar uma exceção que precisa de dados do membro. O objeto permanece válido até depois do lançamento.
Nota: se você estiver usando um compilador mais antigo que o C ++ 11, poderá usar em
std::auto_ptr
vez destd::unique_ptr
, ele fará a mesma coisa.fonte
unique_ptr
de outrounique_ptr
requer uma movimentação, mas não de um ponteiro bruto. A menos que as coisas mudem no C ++ 17?Um dos motivos pelos quais o C ++ foi projetado foi facilitar a reutilização do código. Em geral, o C ++ deve ser escrito para que funcione se a classe é instanciada no heap, em uma matriz ou na pilha. "Excluir isso" é uma prática de codificação muito ruim, porque só funcionará se uma única instância for definida no heap; e é melhor não haver outra instrução de exclusão, que normalmente é usada pela maioria dos desenvolvedores para limpar a pilha. Isso também pressupõe que nenhum programador de manutenção no futuro curará um vazamento de memória percebido falsamente ao adicionar uma instrução de exclusão.
Mesmo que você saiba com antecedência que seu plano atual é alocar apenas uma única instância no heap, e se algum desenvolvedor de boa sorte surgir no futuro e decidir criar uma instância na pilha? Ou, se ele recortar e colar determinadas partes da classe em uma nova classe que ele pretende usar na pilha? Quando o código alcança "delete this", ele apaga e apaga, mas quando o objeto sai do escopo, ele chama o destruidor. O destruidor tentará excluí-lo novamente e, em seguida, você receberá uma mangueira. No passado, fazer algo assim iria estragar não apenas o programa, mas o sistema operacional e o computador precisariam ser reiniciados. De qualquer forma, isso NÃO é altamente recomendado e quase sempre deve ser evitado. Eu teria que estar desesperado, seriamente engessado,
fonte
É permitido (apenas não use o objeto depois disso), mas eu não escreveria esse código na prática. Eu acho que
delete this
deve aparecer apenas em funções que chamourelease
ouRelease
e se parece com:void release() { ref--; if (ref<1) delete this; }
.fonte
Bem, na construção do Component Object Model (COM)
delete this
pode ser uma parte doRelease
método chamado sempre que você deseja liberar o objeto adquirido:fonte
Este é o idioma principal para objetos contados por referência.
A contagem de referência é uma forma forte de coleta de lixo determinística - garante que os objetos gerenciem sua própria vida útil, em vez de depender de ponteiros 'inteligentes' etc. para fazer isso por eles. O objeto subjacente só é acessado através de ponteiros inteligentes "Referência", projetados para que os ponteiros aumentem e diminuam um número inteiro do membro (a contagem de referência) no objeto real.
Quando a última referência cair da pilha ou for excluída, a contagem de referência será zero. O comportamento padrão do seu objeto será uma chamada para "excluir isso" na coleta de lixo - as bibliotecas que eu escrevo fornecem uma chamada "CountIsZero" virtual protegida na classe base, para que você possa substituir esse comportamento por coisas como armazenamento em cache.
A chave para tornar isso seguro não é permitir que os usuários acessem o CONSTRUTOR do objeto em questão (proteja-o), mas faça com que eles chamem algum membro estático - o FACTORY - como "static Reference CreateT (...)". Dessa forma, você sabe com certeza que eles sempre são construídos com "novos" comuns e que nenhum ponteiro bruto está disponível; portanto, "excluir isso" nunca explodirá.
fonte
Você pode fazer isso. No entanto, você não pode atribuir isso. Portanto, a razão pela qual você declara fazer isso "Quero mudar de opinião" parece muito questionável. O melhor método, na minha opinião, seria o objeto que mantém a visão substituir essa visão.
Claro, você está usando objetos RAII e, portanto, não precisa chamar a exclusão ... certo?
fonte
Essa é uma pergunta antiga, respondida, mas o @Alexandre perguntou "Por que alguém iria querer fazer isso?", E pensei em fornecer um exemplo de uso que estou considerando esta tarde.
Código legado. Usa ponteiros nus Obj * obj com um objeto de exclusão no final.
Infelizmente, às vezes, não muitas vezes, preciso manter o objeto vivo por mais tempo.
Estou pensando em torná-lo um ponteiro inteligente contado como referência. Mas haveria muito código para alterar, se eu fosse usar em
ref_cnt_ptr<Obj>
qualquer lugar. E se você misturar Obj * nu e ref_cnt_ptr, poderá excluir o objeto implicitamente quando o último ref_cnt_ptr desaparecer, mesmo que o Obj * ainda esteja vivo.Então, eu estou pensando em criar um explícito_delete_ref_cnt_ptr. Ou seja, um ponteiro contado de referência em que a exclusão é feita apenas em uma rotina de exclusão explícita. Usá-lo no local em que o código existente conhece a vida útil do objeto, bem como no meu novo código que mantém o objeto vivo por mais tempo.
Incrementar e decrementar a contagem de referência à medida que explícita_delete_ref_cnt_ptr é manipulada.
Mas NÃO libera quando a contagem de referência é vista como zero no destruidor explícito_delete_ref_cnt_ptr.
Liberando apenas quando a contagem de referência é zero em uma operação explícita de exclusão. Por exemplo, em algo como:
OK, algo assim. É um pouco incomum ter um tipo de ponteiro contado por referência para não excluir automaticamente o objeto apontado no destruidor rc'ed ptr. Mas parece que isso pode tornar a mistura de ponteiros nus e ponteiros rc'ed um pouco mais segura.
Mas até agora não há necessidade de excluir isso.
Mas então me ocorreu: se o objeto apontado, o apontador, sabe que está sendo contado como referência, por exemplo, se a contagem estiver dentro do objeto (ou em alguma outra tabela), a rotina delete_if_rc0 pode ser um método do objeto de ponta, não o ponteiro (inteligente).
Na verdade, ele não precisa ser um método membro, mas pode ser uma função livre:
(BTW, eu sei que o código não está certo - ele se torna menos legível se eu adicionar todos os detalhes, por isso estou deixando assim.)
fonte
Excluir isso é legal, desde que o objeto esteja na pilha. Você precisaria exigir que o objeto fosse apenas heap. A única maneira de fazer isso é proteger o destruidor - dessa forma, a exclusão pode ser chamada SOMENTE da classe, portanto você precisaria de um método que garanta a exclusão
fonte