Diretrizes gerais para evitar vazamentos de memória em C ++ [fechado]

130

Quais são algumas dicas gerais para garantir que eu não vaze memória nos programas C ++? Como faço para descobrir quem deve liberar memória que foi alocada dinamicamente?

dulipishi
fonte
26
Parece bastante construtivo para mim.
Shoerob
11
Isso é construtivo. E as respostas são apoiadas por fatos, conhecimentos, referências, etc. E veja o número de votos / respostas .. !!
Samitha Chathuranga

Respostas:

40

Em vez de gerenciar a memória manualmente, tente usar ponteiros inteligentes, quando aplicável.
Dê uma olhada no Boost lib , TR1 e ponteiros inteligentes .
Agora, ponteiros inteligentes agora fazem parte do padrão C ++ chamado C ++ 11 .

Andri Möll
fonte
1
Para compilar usando g ++ Um precisa de adicionar param: -std = c ++ 0x
Paweł Szczur
ou você pode compilar com g ++ usando o valor da bandeira -std = c ++ 11
Prabhash Rathore
200

Endosso completamente todos os conselhos sobre RAII e indicadores inteligentes, mas também gostaria de adicionar uma dica de nível um pouco mais alto: a memória mais fácil de gerenciar é a memória que você nunca alocou. Diferente de linguagens como C # e Java, onde praticamente tudo é uma referência, em C ++ você deve colocar objetos na pilha sempre que puder. Como já vi várias pessoas (incluindo o Dr. Stroustrup), a principal razão pela qual a coleta de lixo nunca foi popular em C ++ é que o C ++ bem escrito não produz muito lixo em primeiro lugar.

Não escreva

Object* x = new Object;

ou mesmo

shared_ptr<Object> x(new Object);

quando você pode escrever

Object x;
Ross Smith
fonte
34
Eu gostaria de poder dar a isso um +10. Esse é o maior problema que vejo atualmente com a maioria dos programadores de C ++, e presumo que seja porque eles aprenderam Java antes do C ++.
Kristopher Johnson
Ponto muito interessante - eu me perguntava por que eu tenho problemas de gerenciamento de memória C ++ muito menos frequentemente do que em outros idiomas, mas agora eu vejo o porquê: ele realmente permite coisas para ir para a pilha como a baunilha C.
ArtOfWarfare
Então, o que você faz se escrever Objeto x; e depois quer jogar x fora? digamos que x foi criado no método principal.
Yamcha 11/02
3
@ user1316459 O C ++ também permite que você crie escopos em tempo real. Tudo o que você precisa fazer é acondicionar a vida útil de x dentro de chaves assim: {Object x; x.DoAlgo; } Após o final '}', o destruidor de x será chamado liberando todos os recursos que ele contém. Se x, por si só, for a memória a ser alocada no heap, sugiro agrupá-lo em um unique_ptr para que seja limpo de maneira fácil e apropriada.
David Peterson
1
Robert: sim. Ross não disse "Nunca escreva [código contendo novo]", ele disse "Não escreva [isso] quando você puder apenas [colocá-lo na pilha]". Objetos grandes no heap continuarão sendo a chamada certa na maioria das situações, especialmente para código com alto desempenho.
Codetaku 26/05
104

Use RAII

  • Esqueça a coleta de lixo (use RAII). Observe que até o Garbage Collector também pode vazar (se você esquecer de "anular" algumas referências em Java / C #), e que o Garbage Collector não ajudará a descartar recursos (se você tiver um objeto que adquiriu um identificador para um arquivo, o arquivo não será liberado automaticamente quando o objeto ficar fora do escopo se você não o fizer manualmente em Java ou usar o padrão "dispor" em C #).
  • Esqueça a regra "um retorno por função" . Este é um bom conselho de C para evitar vazamentos, mas está desatualizado no C ++ devido ao uso de exceções (use RAII).
  • E embora o "Padrão de sanduíche" seja um bom conselho de C, ele está desatualizado no C ++ devido ao uso de exceções (use RAII).

Este post parece ser repetitivo, mas em C ++, o padrão mais básico a ser conhecido é o RAII .

Aprenda a usar ponteiros inteligentes, tanto do boost, TR1 quanto do auto_ptr modesto (mas geralmente eficiente o suficiente) (mas você deve conhecer suas limitações).

RAII é a base da segurança de exceção e do descarte de recursos em C ++, e nenhum outro padrão (sanduíche, etc.) fornecerá os dois (e na maioria das vezes, não fornecerá nenhum).

Veja abaixo uma comparação do código RAII e não-RAII:

void doSandwich()
{
   T * p = new T() ;
   // do something with p
   delete p ; // leak if the p processing throws or return
}

void doRAIIDynamic()
{
   std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

void doRAIIStatic()
{
   T p ;
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

Sobre a RAII

Para resumir (após o comentário do Ogre Psalm33 ), o RAII se baseia em três conceitos:

  • Uma vez que o objeto é construído, ele simplesmente funciona! Adquira recursos no construtor.
  • Destruição de objetos é suficiente! Libere recursos no destruidor.
  • É tudo sobre escopos! Objetos com escopo definido (veja o exemplo doRAIIStatic acima) serão construídos na declaração e serão destruídos no momento em que a execução sair do escopo, independentemente da saída (retorno, interrupção, exceção, etc.).

Isso significa que, no código C ++ correto, a maioria dos objetos não será construída newe, em vez disso, será declarada na pilha. E para aqueles construídos usando new, tudo terá um escopo de alguma forma (por exemplo, anexado a um ponteiro inteligente).

Como desenvolvedor, isso é muito poderoso, pois você não precisará se preocupar com o manuseio manual de recursos (como em C ou em alguns objetos em Java que fazem uso intensivo try/ finallypara esse caso) ...

Editar (12-02-2012)

"objetos com escopo ... serão destruídos ... não importa a saída", isso não é inteiramente verdade. existem maneiras de enganar RAII. qualquer sabor de terminate () ignorará a limpeza. exit (EXIT_SUCCESS) é um oxímoro a esse respeito.

- wilhelmtell

wilhelmtell está certo sobre isso: existem maneiras excepcionais de enganar a RAII, todas levando à parada abrupta do processo.

Essas são maneiras excepcionais porque o código C ++ não está repleto de terminação, saída etc., ou, no caso de exceções, queremos uma exceção não tratada para travar o processo e o core despejar sua imagem de memória como está, e não após a limpeza.

Mas ainda precisamos saber sobre esses casos, porque, embora raramente aconteçam, ainda podem acontecer.

(quem chama terminateou exitno código C ++ casual? ... Lembro-me de ter que lidar com esse problema ao jogar com o GLUT : essa biblioteca é muito orientada para C, chegando ao ponto de projetá-la ativamente para tornar as coisas difíceis para desenvolvedores de C ++, como não se importarem sobre empilhar dados alocados ou tomar decisões "interessantes" sobre nunca retornar do loop principal ... Não vou comentar sobre isso) .

paercebal
fonte
A classe T não deve usar RAII para garantir que doRAIIStatic () não vaze memória? Por exemplo, Tp (); p.doSandwich (); Eu realmente não sei muito sobre isso.
285 Daniel O
@Ogre Psalm33: Obrigado pelo comentário. Claro que você está certo. Adicionei os dois links para a página da RAII na Wikipedia e um pequeno resumo do que é RAII.
28410 paercebal
1
@ Shiftbit: Três maneiras, em ordem de preferência: _ _ _ 1. Coloque o objeto real dentro do contêiner STL. _ _ _ 2. Coloque ponteiros inteligentes (shared_ptr) de objetos dentro do contêiner STL. _ _ _ 3. Coloque ponteiros brutos dentro do contêiner STL, mas envolva o contêiner para controlar qualquer acesso aos dados. O wrapper garantirá que o destruidor libere os objetos alocados e os acessadores do wrapper garantirão que nada seja quebrado ao acessar / modificar o contêiner.
paercebal
1
@ Robert: No C ++ 03, você usaria doRAIIDynamic em uma função que deve atribuir a propriedade a uma função filho ou pai (ou escopo global). Ou quando você está recebendo uma interface para um objeto polimorfo através de uma fábrica (retornando um ponteiro inteligente, se ele estiver escrito corretamente). Em C ++ 11, isso é menos o caso, porque você pode fazer o seu móvel objeto, dando assim a propriedade de um objeto declarado na pilha é mais fácil ...
paercebal
2
@ Robert: ... Note que declarar um objeto na pilha não significa que ele não use a pilha internamente (observe a dupla negação ... :-) ...). Por exemplo, std :: string implementado com Small String Optimization terá um buffer "na pilha da classe" para strings pequenas (~ 15 caracteres) e usará um ponteiro para uma memória na pilha para strings maiores ... Mas do lado de fora, std :: string ainda é um tipo de valor que você declara (geralmente) na pilha e usa como usaria um número inteiro (ao contrário de: como usaria uma interface para uma classe polimorfa).
paercebal
25

Você vai querer olhar para ponteiros inteligentes, como os ponteiros inteligentes do boost .

Ao invés de

int main()
{ 
    Object* obj = new Object();
    //...
    delete obj;
}

O boost :: shared_ptr será excluído automaticamente quando a contagem de referência for zero:

int main()
{
    boost::shared_ptr<Object> obj(new Object());
    //...
    // destructor destroys when reference count is zero
}

Observe minha última observação, "quando a contagem de referência for zero, que é a parte mais legal. Portanto, se você tiver vários usuários do seu objeto, não precisará acompanhar se o objeto ainda está em uso. Depois que ninguém se refere ao seu ponteiro compartilhado, ele é destruído.

Esta não é uma panacéia, no entanto. Embora você possa acessar o ponteiro base, não desejaria passá-lo para uma API de terceiros, a menos que estivesse confiante com o que estava fazendo. Muitas vezes, as coisas de "postagem" em algum outro encadeamento para que o trabalho seja feito APÓS o término da criação. Isso é comum com PostThreadMessage no Win32:

void foo()
{
   boost::shared_ptr<Object> obj(new Object()); 

   // Simplified here
   PostThreadMessage(...., (LPARAM)ob.get());
   // Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}

Como sempre, use seu boné de pensamento com qualquer ferramenta ...

Doug T.
fonte
12

Leia o RAII e certifique-se de entender.

Hank
fonte
11

A maioria dos vazamentos de memória resulta da falta de clareza sobre a propriedade e a duração do objeto.

A primeira coisa a fazer é alocar na pilha sempre que puder. Isso lida com a maioria dos casos em que você precisa alocar um único objeto para alguma finalidade.

Se você precisar 'novo' um objeto, na maioria das vezes ele terá um único proprietário óbvio pelo resto de sua vida útil. Para esta situação, costumo usar vários modelos de coleções projetados para 'possuir' objetos armazenados neles pelo ponteiro. Eles são implementados com o vetor STL e os contêineres de mapas, mas têm algumas diferenças:

  • Essas coleções não podem ser copiadas ou atribuídas. (uma vez que eles contenham objetos.)
  • Ponteiros para objetos são inseridos neles.
  • Quando a coleção é excluída, o destruidor é chamado pela primeira vez em todos os objetos da coleção. (Tenho outra versão em que afirma se destruída e não vazia.)
  • Como eles armazenam ponteiros, você também pode armazenar objetos herdados nesses contêineres.

Meu problema com o STL é que ele é tão focado nos objetos Value, enquanto na maioria dos aplicativos os objetos são entidades únicas que não possuem semântica de cópia significativa necessária para uso nesses contêineres.

Jeroen Dirks
fonte
10

Bah, vocês jovens e seus novos colecionadores de lixo ...

Regras muito fortes sobre "propriedade" - qual objeto ou parte do software tem o direito de excluir o objeto. Comentários claros e nomes de variáveis ​​sábios para tornar óbvio se um ponteiro "possui" ou é "apenas olhe, não toque". Para ajudar a decidir quem possui o quê, siga o máximo possível o padrão "sanduíche" em todas as sub-rotinas ou métodos.

create a thing
use that thing
destroy that thing

Às vezes é necessário criar e destruir em lugares amplamente diferentes; Eu acho difícil evitar isso.

Em qualquer programa que exija estruturas de dados complexas, eu crio uma árvore estrita e clara de objetos contendo outros objetos - usando ponteiros "proprietários". Essa árvore modela a hierarquia básica dos conceitos de domínio de aplicativo. Exemplo: uma cena 3D possui objetos, luzes, texturas. No final da renderização, quando o programa é encerrado, existe uma maneira clara de destruir tudo.

Muitos outros ponteiros são definidos conforme necessário sempre que uma entidade precisa acessar outra, para varrer arays ou o que for; estes são os "apenas olhando". Para o exemplo da cena 3D - um objeto usa uma textura, mas não possui; outros objetos podem usar a mesma textura. A destruição de um objeto não invoca a destruição de nenhuma textura.

Sim, é demorado, mas é o que eu faço. Eu raramente tenho vazamentos de memória ou outros problemas. Mas então trabalho na arena limitada de software científico, de aquisição de dados e gráficos de alto desempenho. Não costumo negociar transações como bancos e comércio eletrônico, GUIs orientadas a eventos ou caos assíncrono em rede. Talvez os novos caminhos tenham uma vantagem por lá!

DarenW
fonte
Eu concordo totalmente. Trabalhando em um ambiente incorporado, você também pode não ter o luxo de bibliotecas de terceiros.
simon
6
Discordo. na parte "use that thing", se um retorno ou uma exceção for lançada, você perderá a desalocação. Quanto ao desempenho, o std :: auto_ptr não custaria nada. Não que eu nunca codifique da mesma maneira que você. Só que existe uma diferença entre código 100% e 99% seguro. :-)
paercebal 17/09/08
8

Ótima pergunta!

se você estiver usando c ++ e estiver desenvolvendo um aplicativo alto de CPU e memória em tempo real (como jogos), precisará criar seu próprio Gerenciador de Memória.

Acho que o melhor que você pode fazer é mesclar algumas obras interessantes de vários autores, posso dar uma dica:

  • Alocador de tamanho fixo é muito discutido, em todos os lugares da rede

  • A alocação de objetos pequenos foi introduzida por Alexandrescu em 2001 em seu livro perfeito "Design moderno em c ++"

  • Um grande avanço (com o código-fonte distribuído) pode ser encontrado em um incrível artigo na Game Programming Gem 7 (2008) chamado "High Performance Heap alocador", escrito por Dimitar Lazarov

  • Uma ótima lista de recursos pode ser encontrada em neste artigo

Não comece a escrever um alocador inútil inútil por si mesmo ... DOCUMENTO-SE primeiro.

ugasoft
fonte
5

Uma técnica que se tornou popular no gerenciamento de memória em C ++ é o RAII . Basicamente, você usa construtores / destruidores para lidar com a alocação de recursos. É claro que existem outros detalhes desagradáveis ​​em C ++ devido à segurança de exceções, mas a idéia básica é bastante simples.

A questão geralmente se resume a uma questão de propriedade. Eu recomendo a leitura das séries Effective C ++ de Scott Meyers e Modern C ++ Design de Andrei Alexandrescu.

Jason Dagit
fonte
5

Já existe muito sobre como não vazar, mas se você precisar de uma ferramenta para ajudá-lo a rastrear vazamentos, dê uma olhada:

fabiopedrosa
fonte
O BoundsChecker está 404ing.
precisa saber é o seguinte
4

Use ponteiros inteligentes em todos os lugares que puder! Classes inteiras de vazamento de memória simplesmente desaparecem.

DougN
fonte
4

Compartilhe e conheça as regras de propriedade da memória em seu projeto. O uso das regras COM garante a melhor consistência (os parâmetros [de entrada] pertencem ao chamador, o destinatário deve copiar; os parâmetros [out] pertencem ao chamador, o destinatário deve fazer uma cópia se manter uma referência; etc.)

Seth Morris
fonte
4

O valgrind é uma boa ferramenta para verificar também os vazamentos de memória dos programas em tempo de execução.

Está disponível na maioria dos tipos de Linux (incluindo Android) e no Darwin.

Se você costuma escrever testes de unidade para seus programas, deve adquirir o hábito de executar sistematicamente o valgrind nos testes. Potencialmente, evitará muitos vazamentos de memória em um estágio inicial. Também é geralmente mais fácil identificá-los em testes simples que em um software completo.

Obviamente, este conselho permanece válido para qualquer outra ferramenta de verificação de memória.

kriss
fonte
3

Além disso, não use memória alocada manualmente se houver uma classe de biblioteca padrão (por exemplo, vetor). Certifique-se de violar essa regra que possui um destruidor virtual.

Joseph
fonte
2

Se você não pode / não usa um ponteiro inteligente para algo (embora isso deva ser uma grande bandeira vermelha), digite seu código com:

allocate
if allocation succeeded:
{ //scope)
     deallocate()
}

Isso é óbvio, mas certifique-se de digitá-lo antes de digitar qualquer código no escopo

Seth Morris
fonte
2

Uma fonte frequente desses erros é quando você tem um método que aceita uma referência ou ponteiro para um objeto, mas deixa a propriedade pouco clara. Convenções de estilo e comentários podem tornar isso menos provável.

Seja o caso especial em que a função assume a propriedade do objeto. Em todas as situações em que isso acontece, certifique-se de escrever um comentário ao lado da função no arquivo de cabeçalho indicando isso. Você deve se esforçar para garantir que, na maioria dos casos, o módulo ou a classe que aloca um objeto também seja responsável por desalocá-lo.

O uso de const pode ajudar bastante em alguns casos. Se uma função não modificar um objeto e não armazenar uma referência a ele que persista após o retorno, aceite uma referência const. Ao ler o código do chamador, será óbvio que sua função não aceitou a propriedade do objeto. Você poderia ter a mesma função aceitar um ponteiro não-const e o chamador pode ou não ter assumido que o chamado aceitou a propriedade, mas com uma referência const, não há dúvida.

Não use referências não-const nas listas de argumentos. Não é muito claro ao ler o código do chamador que o receptor pode ter mantido uma referência ao parâmetro.

Não concordo com os comentários que recomendam indicadores de referência contados. Isso geralmente funciona bem, mas quando você tem um bug e não funciona, especialmente se o seu destruidor faz algo não trivial, como em um programa multithread. Definitivamente, tente ajustar seu design para não precisar de contagem de referência, se não for muito difícil.

Jonathan
fonte
2

Dicas em ordem de importância:

Dica # 1 Lembre-se sempre de declarar seus destruidores "virtuais".

Dica 2: use RAII

Dica # 3 Use os smartpointers da boost

Dica # 4 Não escreva seus próprios Smartpointers de buggy, use boost (em um projeto em que estou no momento não posso usar o boost, e sofri a necessidade de depurar meus próprios ponteiros inteligentes, eu definitivamente não aceitaria a mesma rota novamente, mas novamente agora não posso adicionar impulso às nossas dependências)

Dica # 5 Se algum trabalho ocasional / crítico para o desempenho (como em jogos com milhares de objetos) funcionar, observe o contêiner de ponteiro de impulso de Thorsten Ottosen

Dica # 6 Encontre um cabeçalho de detecção de vazamento para sua plataforma de escolha, como o cabeçalho "vld" da Visual Leak Detection

Robert Gould
fonte
Posso estar perdendo um truque, mas como 'jogo' e 'não crítico para o desempenho' podem estar na mesma frase?
23611 Adam Naylor
Os jogos são um exemplo do cenário crítico, é claro. Pode ter deixado de ser claro lá
Robert Gould
A dica nº 1 deve ser aplicada apenas se a classe tiver pelo menos um método virtual. Eu nunca imporia um destruidor virtual inútil a uma classe que não deveria servir como classe base em uma árvore de herança polimórfica.
antred
1

Se puder, use boost shared_ptr e C ++ auto_ptr padrão. Eles transmitem semântica de propriedade.

Quando você retorna um auto_ptr, está dizendo ao chamador que está dando a ele propriedade da memória.

Quando você retorna um shared_ptr, está dizendo ao chamador que você tem uma referência a ele e ele faz parte da propriedade, mas não é apenas responsabilidade deles.

Essas semânticas também se aplicam aos parâmetros. Se o chamador passar um auto_ptr, ele estará lhe dando propriedade.

Justin Rudd
fonte
1

Outros mencionaram maneiras de evitar vazamentos de memória em primeiro lugar (como ponteiros inteligentes). Mas uma ferramenta de análise de perfil e de memória geralmente é a única maneira de rastrear problemas de memória depois que você os tiver.

O memcheck do Valgrind é excelente e gratuito.

eli
fonte
1

Somente para MSVC, adicione o seguinte na parte superior de cada arquivo .cpp:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

Em seguida, ao depurar com o VS2003 ou superior, você será informado sobre quaisquer vazamentos quando o programa sair (ele rastreia novo / exclui). É básico, mas me ajudou no passado.

Roubar
fonte
1

valgrind (disponível apenas para plataformas * nix) é um verificador de memória muito bom

Ronny Brendel
fonte
1

Se você deseja gerenciar sua memória manualmente, você tem dois casos:

  1. Eu criei o objeto (talvez indiretamente, chamando uma função que aloca um novo objeto), eu o uso (ou uma função que eu chamo de uso) e liberto-o.
  2. Alguém me deu a referência, então não devo libertá-la.

Se você precisar violar alguma dessas regras, documente-a.

É tudo sobre propriedade de ponteiro.

Null303
fonte
1
  • Tente evitar alocar objetos dinamicamente. Desde que as classes tenham construtores e destruidores apropriados, use uma variável do tipo de classe, não um ponteiro para ela, e evite alocação e desalocação dinâmica porque o compilador fará isso por você.
    Na verdade, esse também é o mecanismo usado por "ponteiros inteligentes" e referido como RAII por alguns dos outros escritores ;-).
  • Quando você passa objetos para outras funções, prefira parâmetros de referência sobre ponteiros. Isso evita alguns erros possíveis.
  • Declare parâmetros const, sempre que possível, especialmente ponteiros para objetos. Dessa forma, os objetos não podem ser liberados "acidentalmente" (exceto se você expulsar a const ;-))).
  • Minimize o número de locais no programa em que você faz a alocação e desalocação de memória. Por exemplo. se você alocar ou liberar o mesmo tipo várias vezes, escreva uma função para ela (ou um método de fábrica ;-)).
    Dessa forma, você pode criar uma saída de depuração (quais endereços são alocados e desalocados, ...) facilmente, se necessário.
  • Use uma função de fábrica para alocar objetos de várias classes relacionadas a partir de uma única função.
  • Se suas classes tiverem uma classe base comum com um destruidor virtual, você poderá liberá-las usando a mesma função (ou método estático).
  • Verifique seu programa com ferramentas como purificar (infelizmente muitos $ / € / ...).
mh
fonte
0

Você pode interceptar as funções de alocação de memória e verificar se há algumas zonas de memória não liberadas na saída do programa (embora não seja adequado para todos os aplicativos).

Isso também pode ser feito em tempo de compilação, substituindo os operadores new e delete e outras funções de alocação de memória.

Por exemplo, verifique neste site [Depurando alocação de memória em C ++] Nota: Há um truque para o operador de exclusão também algo como isto:

#define DEBUG_DELETE PrepareDelete(__LINE__,__FILE__); delete
#define delete DEBUG_DELETE

Você pode armazenar em algumas variáveis ​​o nome do arquivo e quando o operador de exclusão sobrecarregado saberá de onde foi chamado. Dessa forma, você pode rastrear cada exclusão e malloc do seu programa. No final da sequência de verificação de memória, você deve poder relatar qual bloco de memória alocado não foi 'excluído', identificando-o pelo nome do arquivo e pelo número da linha, que é o que você deseja.

Você também pode tentar algo como o BoundsChecker no Visual Studio, que é bastante interessante e fácil de usar.

INS
fonte
0

Envolvemos todas as nossas funções de alocação com uma camada que acrescenta uma breve sequência na frente e uma bandeira sentinela no final. Assim, por exemplo, você teria uma chamada para "myalloc (pszSomeString, iSize, iAlignment); ou novo (" description ", iSize) MyObject (); que aloca internamente o tamanho especificado, mais espaço suficiente para o cabeçalho e o sentinela. , não esqueça de comentar isso para compilações sem depuração! É preciso um pouco mais de memória para fazer isso, mas os benefícios superam os custos.

Isso tem três benefícios - primeiro, permite rastrear fácil e rapidamente o código que está vazando, fazendo pesquisas rápidas por códigos alocados em determinadas 'zonas', mas não limpos quando essas zonas deveriam ter sido liberadas. Também pode ser útil detectar quando um limite foi substituído, verificando se todos os sentinelas estão intactos. Isso nos salvou várias vezes ao tentar encontrar essas falhas ou erros de matriz bem ocultos. O terceiro benefício é rastrear o uso da memória para ver quem são os grandes jogadores - um agrupamento de determinadas descrições em um MemDump informa quando o 'som' ocupa muito mais espaço do que o esperado, por exemplo.

brilho da tela
fonte
0

C ++ é projetado RAII em mente. Não há realmente nenhuma maneira melhor de gerenciar memória em C ++, eu acho. Mas tome cuidado para não alocar pedaços muito grandes (como objetos de buffer) no escopo local. Isso pode causar estouros de pilha e, se houver uma falha nos limites de verificação ao usar esse pedaço, você poderá sobrescrever outras variáveis ​​ou endereços de retorno, o que leva a todos os tipos de falhas de segurança.

artificialidiot
fonte
0

Um dos únicos exemplos de alocação e destruição em locais diferentes é a criação de encadeamentos (o parâmetro que você passa). Mas mesmo neste caso é fácil. Aqui está a função / método que cria um thread:

struct myparams {
int x;
std::vector<double> z;
}

std::auto_ptr<myparams> param(new myparams(x, ...));
// Release the ownership in case thread creation is successfull
if (0 == pthread_create(&th, NULL, th_func, param.get()) param.release();
...

Aqui, ao invés, a função thread

extern "C" void* th_func(void* p) {
   try {
       std::auto_ptr<myparams> param((myparams*)p);
       ...
   } catch(...) {
   }
   return 0;
}

Muito fácil, não é? Caso a criação do encadeamento falhe, o recurso será liberado (excluído) pelo auto_ptr, caso contrário, a propriedade será passada para o encadeamento. E se o encadeamento for tão rápido que, após a criação, libere o recurso antes do

param.release();

é chamado na principal função / método? Nada! Porque 'informaremos' o auto_ptr para ignorar a desalocação. O gerenciamento de memória C ++ é fácil, não é? Felicidades,

Ema!

Emanuele Oriani
fonte
0

Gerencie a memória da mesma maneira que gerencia outros recursos (identificadores, arquivos, conexões db, soquetes ...). A GC também não ajudaria você.

Nemanja Trifunovic
fonte
-3

Exatamente um retorno de qualquer função. Dessa forma, você pode fazer a desalocação lá e nunca perder.

Caso contrário, é muito fácil cometer um erro:

new a()
if (Bad()) {delete a; return;}
new b()
if (Bad()) {delete a; delete b; return;}
... // etc.
Seth Morris
fonte
Sua resposta não corresponde ao código de exemplo aqui? Concordo com a resposta "apenas um retorno", mas o código de exemplo está mostrando o que NÃO fazer.
simon
1
O argumento do C ++ RAII é exatamente para evitar o tipo de código que você escreveu. Em C, esta é provavelmente a coisa certa a fazer. Mas em C ++, seu código é defeituoso. Por exemplo: E se o novo b () for lançado? Você vaza a.
paercebal