Um mergulho na implementação da STL revela a utilização do operator new. A análise de um fragmento de código template demonstra sua aplicação:
template<typename Type>
inline void release_memory(Type* ptr) {
::operator delete(ptr);
// Note: operator delete também pode ser sobrecarregado
}
Isso levou a uma investigação mais aprofundada desses operadores. Uma dúvida inicial surgiu sobre a finalidade do operador de escopo global ::. O seguinte teste esclareceu sua função:
#include <iostream>
class Base {
public:
void executar() {
std::cout << "Base" << std::endl;
}
};
void executar() {
std::cout << "Global" << std::endl;
}
class Derivada : public Base {
public:
void executar() {
std::cout << "Derivada" << std::endl;
::executar(); // Chama explicitamente a função global
}
};
int main() {
Derivada* obj = new Derivada();
obj->executar();
delete obj;
return 0;
}
O resultado da execução é:
Derivada
Global
Isso demonstra que :: é usado para desambiguar e acessar símbolos no escopo global, ignorando definições locais ou de classe com o mesmo nome.
A sobrecarga do operator new e operator delete permite o controle customizado sobre a alocação e liberação de memória. A busca por uma sobrecarga segue o princípio de cobertura de escopo: o compilador primeiro procura na classe, depois no escopo global. Se uma correspondência for encontrada no escopo da classe, a busca para, mesmo que um operador global compatível exista.
#include <iostream>
#include <new>
#include <vector>
class GerenciadorMemoria {
public:
// Sobrecarga do new padrão
void* operator new(std::size_t tamanho) {
std::cout << "Gerenciador: alocando bytes: " << tamanho << "\n";
void* ponteiro = ::operator new(tamanho);
return ponteiro;
}
// Sobrecarga do new com nothrow
void* operator new(std::size_t tamanho, const std::nothrow_t& tag) noexcept {
std::cout << "Gerenciador: alocando (nothrow) bytes: " << tamanho << "\n";
return ::operator new(tamanho, tag);
}
// Placement new - constrói no endereço fornecido
void* operator new(std::size_t /*tamanho*/, void* endereco_existente) {
std::cout << "Gerenciador: placement new\n";
return endereco_existente;
}
// Sobrecarga do delete padrão
void operator delete(void* ponteiro) {
std::cout << "Gerenciador: liberando memoria\n";
::operator delete(ponteiro);
}
// Sobrecarga do array new
void* operator new[](std::size_t tamanho) {
std::cout << "Gerenciador: alocando array de bytes: " << tamanho << "\n";
return ::operator new[](tamanho);
}
// Opcional: sobrecarga para array delete
void operator delete[](void* ponteiro) {
std::cout << "Gerenciador: liberando array de memoria\n";
::operator delete[](ponteiro);
}
// Destrutor para demonstração
~GerenciadorMemoria() {
std::cout << "Gerenciador destruido\n";
}
};
int main() {
// 1. Usar o new sobrecarregado da classe
GerenciadorMemoria* obj1 = new GerenciadorMemoria;
delete obj1;
std::cout << "---\n";
// 2. Usar o placement new (construção em memória pré-alocada)
char buffer[sizeof(GerenciadorMemoria)];
GerenciadorMemoria* obj2 = new (buffer) GerenciadorMemoria;
// Para placement new, o delete normal NÃO deve ser chamado.
// Chamamos o destrutor explicitamente.
obj2->~GerenciadorMemoria();
std::cout << "---\n";
// 3. Alocar um array
GerenciadorMemoria* arr = new GerenciadorMemoria[3];
delete[] arr;
std::cout << "---\n";
// 4. Forçar o uso do operador new global, ignorando a sobrecarga da classe
GerenciadorMemoria* obj3 = ::new GerenciadorMemoria;
::delete obj3;
return 0;
}
A saída deste programa ilustra as diferentes chamadas realizadas pelos operadores sobrecarregados. Conceitos cruciais incluem:
- Cobertura de Escopo: Se uma classe define
operator new(size_t), chamadas comonew MinhaClassea utilizam. Chamarnew(std::nothrow) MinhaClasseexigirá uma sobrecarga correspondente na classe; caso contrário, um erro de compilação ocorrerá, pois a busca para na classe e não prossegue para o escopo global. - Placement new: Permite a construção de um objeto em um endereço de memória já alocado (ex.: uma pilha, um pool ou memória compartilhada). Seu operador de exclusão correspondente é frequentemente um no-op ou realiza apenas a destruição do objeto, não a desalocação.
- Encadeamento com o Global: A maioria das sobrecargas delega a alocação ou desalocação real para as versões globais (
::operator new,::operator delete) após adicionar lógica de rastreamento, logging ou gerenciamento especializado.