Pergunta

Aprendi na faculdade que você sempre precisa liberar seus objetos não utilizados, mas não como realmente faz isso.Por exemplo, estruturar seu código corretamente e assim por diante.Existem regras gerais sobre como lidar com ponteiros em C++?

Atualmente não tenho permissão para usar o boost.Tenho que me ater ao C++ puro porque a estrutura que estou usando proíbe qualquer uso de genéricos.

Foi útil?

Solução

Trabalhei com o sistema operacional Symbian embarcado, que possuía um excelente sistema para isso, baseado inteiramente nas convenções do desenvolvedor.

  1. Apenas um objeto possuirá um ponteiro.Por padrão, este é o criador.
  2. A propriedade pode ser transmitida.Para indicar a passagem de propriedade, o objeto é passado como um ponteiro na assinatura do método (por exemplo,void Foo(Barra *zonk);).
  3. O proprietário decidirá quando excluir o objeto.
  4. Para passar um objeto para um método apenas para uso, o objeto é passado como uma referência na assinatura do método (por exemplo,void Foo(Bat &zonk);).
  5. Classes não-proprietárias podem armazenar referências (nunca ponteiros) para objetos que recebem apenas quando podem ter certeza de que o proprietário não os destruirá durante o uso.

Basicamente, se uma classe simplesmente usa algo, ela usa uma referência.Se uma classe possui algo, ela usa um ponteiro.

Funcionou lindamente e foi um prazer usar.Problemas de memória eram muito raros.

Outras dicas

Regras:

  1. Sempre que possível, use umponteiro inteligente.Boost tem algunsBons.
  2. Se você não pode usar um ponteiro inteligente, nulo seu ponteiro depois de excluí -lo.
  3. Nunca trabalhe em qualquer lugar que não permita usar a regra 1.

Se alguém desautorizar a regra 1, lembre-se de que se você pegar o código de outra pessoa, alterar os nomes das variáveis ​​e excluir os avisos de direitos autorais, ninguém jamais notará.A menos que seja um projeto escolar, onde eles realmente verificam esse tipo de travessura com ferramentas bastante sofisticadas.Veja também, essa questão.

Eu adicionaria outra regra aqui:

  • Não crie/exclua um objeto quando um objeto automático funcionar perfeitamente.

Descobrimos que programadores que são novos em C++, ou programadores vindos de linguagens como Java, parecem aprender coisas novas e então usá-las obsessivamente sempre que desejam criar qualquer objeto, independentemente do contexto.Isto é especialmente pernicioso quando um objeto é criado localmente dentro de uma função apenas para fazer algo útil.Usar new dessa maneira pode ser prejudicial ao desempenho e tornar muito fácil a introdução de vazamentos de memória bobos quando a exclusão correspondente for esquecida.Sim, ponteiros inteligentes podem ajudar com o último, mas não resolverão os problemas de desempenho (assumindo que new/delete ou equivalente seja usado nos bastidores).Curiosamente (bem, talvez), descobrimos que excluir geralmente tende a ser mais caro do que novo ao usar o Visual C++.

Parte dessa confusão também vem do fato de que as funções que elas chamam podem receber ponteiros, ou mesmo ponteiros inteligentes, como argumentos (quando as referências talvez seriam melhores/mais claras).Isso os faz pensar que precisam "criar" um ponteiro (muitas pessoas parecem pensar que é isso que new faz) para poder passar um ponteiro para uma função.Claramente, isso requer algumas regras sobre como as APIs são escritas para tornar as convenções de chamada tão inequívocas quanto possível, que são reforçadas com comentários claros fornecidos com o protótipo da função.

No caso geral (gerenciamento de recursos, onde o recurso não é necessariamente memória), você precisa estar familiarizado com o Padrão RAII.Esta é uma das informações mais importantes para desenvolvedores C++.

Em geral, evite alocar do heap, a menos que seja necessário.Se for necessário, use a contagem de referências para objetos que têm vida longa e precisam ser compartilhados entre diversas partes do seu código.

Às vezes você precisa alocar objetos dinamicamente, mas eles só serão usados ​​dentro de um determinado período de tempo.Por exemplo, em um projeto anterior eu precisei criar uma representação complexa na memória de um esquema de banco de dados - basicamente um gráfico cíclico complexo de objetos.No entanto, o gráfico só era necessário durante uma conexão com o banco de dados, após a qual todos os nós poderiam ser liberados de uma só vez.Nesse tipo de cenário, um bom padrão a ser usado é algo que eu chamo de "idioma local do GC". Não tenho certeza se ele tem um nome "oficial", pois é algo que eu só vi no meu próprio código e no cacau (veja NSAutoreleasePool na referência Cocoa da Apple).

Resumindo, você cria um objeto "coletor" que mantém ponteiros para os objetos temporários que você aloca usando new.Geralmente está vinculado a algum escopo em seu programa, seja um escopo estático (por exemplo,-- como um objeto alocado na pilha que implementa o idioma RAII) ou dinâmico (por exemplo,-- vinculado ao tempo de vida de uma conexão de banco de dados, como no meu projeto anterior).Quando o objeto "coletor" é liberado, seu destruidor libera todos os objetos para os quais ele aponta.

Além disso, assim como o DrPizza, acho que a restrição de não usar modelos é muito severa.No entanto, tendo feito muito desenvolvimento em versões antigas do Solaris, AIX e HP-UX (recentemente - sim, essas plataformas ainda estão vivas na Fortune 50), posso dizer que se você realmente se preocupa com a portabilidade, você deve usar modelos o mínimo possível.Porém, usá-los para contêineres e ponteiros inteligentes deve ser aceitável (funcionou para mim).Sem modelos, a técnica que descrevi é mais difícil de implementar.Seria necessário que todos os objetos gerenciados pelo "coletor" derivassem de uma classe base comum.

Bom dia,

Eu sugiro a leitura das seções relevantes de "Effective C++", de Scott Meyers.Fácil de ler e ele cobre algumas dicas interessantes para prender os incautos.

Também estou intrigado com a falta de modelos.Portanto, não há STL ou Boost.Uau.

Aliás, fazer com que as pessoas concordem com as convenções é uma excelente ideia.Assim como fazer com que todos concordem com as convenções para OOD.Aliás, a última edição do Effective C++ não possui o excelente capítulo sobre convenções OOD que a primeira edição tinha, o que é uma pena, por exemplo.convenções como herança virtual pública sempre modela um relacionamento "isa".

Roubar

  • Quando você precisar usar a memória gerencial manualmente, certifique -se de ligar para excluir o mesmo escopo/função/classe/módulo, que se aplica primeiro, por exemplo:
  • Deixe o chamador de uma função alocar a memória preenchida por ela, não retorne ponteiros novos.
  • Sempre chame delete no mesmo exe/dll em que você chamou new, caso contrário você poderá ter problemas com corrupções de heap (diferentes bibliotecas de tempo de execução incompatíveis).

você pode derivar tudo de alguma classe base que implemente funcionalidade semelhante a um ponteiro inteligente (usando métodos ref()/unref() e um contador.

Todos os pontos destacados pelo @Timbo são importantes ao projetar essa classe base.

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