novo e excluir (C ++) - new and delete (C++)


Na linguagem de programação C ++ , new e delete são um par de construções de linguagem que realizam alocação dinâmica de memória , construção e destruição de objetos .

Visão geral

Exceto por um formulário denominado "novo posicionamento" , o novo operador denota uma solicitação de alocação de memória no heap de um processo . Se houver memória suficiente disponível, new inicializa a memória, chamando construtores de objeto se necessário, e retorna o endereço para a memória recém-alocada e inicializada. Uma nova solicitação, em sua forma mais simples, tem a seguinte aparência:

p = new T;

onde p é um ponteiro declarado anteriormente do tipo T (ou algum outro tipo ao qual um ponteiro T pode ser atribuído, como uma superclasse de T ). O construtor padrão para T , se houver, é chamado para construir uma instância de T no buffer de memória alocado.

Se não houver memória suficiente disponível no armazenamento gratuito para um objeto do tipo T , a nova solicitação indica falha, lançando uma exceção do tipo std :: bad_alloc . Isso elimina a necessidade de verificar explicitamente o resultado de uma alocação.

A contraparte de desalocação de new é delete , que primeiro chama o destruidor (se houver) em seu argumento e, em seguida, retorna a memória alocada por new de volta ao armazenamento gratuito. Cada chamada para new deve ser correspondida por uma chamada para delete ; deixar de fazer isso causa vazamentos de memória .

A nova sintaxe possui diversas variantes que permitem um controle mais preciso sobre a alocação de memória e a construção de objetos. Uma sintaxe semelhante à chamada de função é usada para chamar um construtor diferente do padrão e passar argumentos, por exemplo,

p = new T(argument);

chama um construtor T de argumento único em vez do construtor padrão ao inicializar o buffer recém-alocado.

Uma variante diferente aloca e inicializa matrizes de objetos em vez de objetos únicos:

p = new T [N];

Isso solicita um buffer de memória do armazenamento gratuito que é grande o suficiente para conter uma matriz contígua de N objetos do tipo T , de forma contígua, e chama o construtor padrão em cada elemento da matriz.

A memória alocada com o novo [] deve ser desalocada com o operador delete [] , em vez de excluir . Usar o formulário inadequado resulta em comportamento indefinido . Os compiladores C ++ não precisam gerar uma mensagem de diagnóstico por usar o formulário incorreto.

O padrão C ++ 11 especifica uma sintaxe adicional,

p = new T[N] {initializer1, ..., initializerN};

que inicializa cada p [ i ] para inicializar i + 1 .

Manipulação de erros

Se new não puder encontrar memória suficiente para atender a uma solicitação de alocação, ele pode relatar seu erro de três maneiras distintas. Em primeiro lugar, o padrão ISO C ++ permite que os programas registrem uma função personalizada chamada new_handler com o tempo de execução C ++ ; em caso afirmativo, essa função é chamada sempre que new encontra um erro. O new_handler pode tentar disponibilizar mais memória ou encerrar o programa se não puder.

Se nenhum new_handler estiver instalado, new em vez disso lança uma exceção do tipo std :: bad_alloc . Assim, o programa não precisa verificar o valor do ponteiro retornado, como é o hábito em C ; se nenhuma exceção foi lançada, a alocação foi bem-sucedida.

O terceiro método de tratamento de erros é fornecido pela forma variante new (std :: nothrow) , que especifica que nenhuma exceção deve ser lançada; em vez disso, um ponteiro nulo é retornado para sinalizar um erro de alocação.

Sobrecarregando

O novo operador pode ser sobrecarregado para que tipos específicos (classes) usem algoritmos de alocação de memória personalizados para suas instâncias. Por exemplo, o seguinte é uma variante do padrão singleton em que a primeira nova chamada Singleton aloca uma instância e todas as chamadas subsequentes retornam a mesma instância:

#include <cstdlib>
#include <cstddef>

class Singleton {
public:
  static void* operator new(std::size_t size) {
    if (!instance) {
      instance = std::malloc(size);
    }
    refcount++;
    return instance;
  }

  static void operator delete(void*) noexcept {
    if (--refcount == 0) {
      std::free(instance);
      instance = nullptr;
    }
  }

private:
  static void* instance = nullptr;
  static std::size_t refcount = 0;
};

Esse recurso estava disponível desde o início da história do C ++, embora o mecanismo de sobrecarga específico tenha mudado. Ele foi adicionado à linguagem porque os programas C ++ orientados a objetos tendiam a alocar muitos objetos pequenos com new , que usava internamente o alocador C (consulte § Relação com malloc e free ); que, no entanto, foi otimizado para menos e maiores alocações realizadas por programas C típicos. Stroustrup relatou que nas primeiras aplicações, a função C malloc era "o gargalo de desempenho mais comum em sistemas reais", com programas gastando até 50% de seu tempo nessa função.

void *operator new(size_t size)

A construção da linguagem C ++ que apenas aloca memória é chamada . É usado por novos na fase de alocação. Ele pode ser substituído por classe ou globalmente para definir um alocador de memória específico. void *operator new(size_t size)

Relação com malloc e livre

Como o C ++ padrão inclui a biblioteca padrão C , as rotinas de alocação de memória dinâmica C malloc , calloc , realloc e free também estão disponíveis para programadores C ++. O uso dessas rotinas é desencorajado para a maioria dos usos, uma vez que não realizam inicialização e destruição de objetos. new e delete foram, de fato, introduzidos na primeira versão do C ++ (então chamada de " C com classes ") para evitar a necessidade de inicialização manual do objeto.

Em contraste com as rotinas C, que permitem aumentar ou diminuir um array alocado com realloc , não é possível alterar o tamanho de um buffer de memória alocado por new [] . Em vez disso, a biblioteca padrão C ++ fornece um array dinâmico (coleção) que pode ser estendido ou reduzido em sua classe de modelo std :: vector .

O padrão C ++ não especifica nenhuma relação entre new / delete e as rotinas de alocação de memória C, mas new e delete são tipicamente implementados como wrappers em torno de malloc e free . Misturar as duas famílias de operações, por exemplo, liberar uma nova memória alocada ou excluir a memória malloc , causa um comportamento indefinido e, na prática, pode levar a vários resultados catastróficos, como falha em liberar bloqueios e, portanto, deadlock .

Veja também

Referências