1. Classes Não Copiáveis
Em diversos cenários de desenvolvimento, como no gerenciamento de recursos exclusivos (file descriptors, sockets), é necessário garantir que um objeto não seja duplicado. Para impedir a cópia, devemos desativar o construtor de cópia e o operador de atribuição.
class BloqueioDeCopia {
public:
BloqueioDeCopia() = default;
// Desativando explicitamente a cópia e a atribuição (C++11)
BloqueioDeCopia(const BloqueioDeCopia&) = delete;
BloqueioDeCopia& operator=(const BloqueioDeCopia&) = delete;
};
2. Classes com Alocação Exclusiva no Heap
Para forçar que um objeto seja criado apenas na memória dinâmica (heap), o construtor deve ser tornado privado. O acesso à criação do objeto é então disponibilizado através de um método estático que retorna um ponteiro.
class ApenasHeap {
public:
static ApenasHeap* CriarInstancia() {
return new ApenasHeap();
}
// Método para destruir o objeto, já que o destrutor pode ser privado
void Destruir() {
delete this;
}
private:
ApenasHeap() {}
// Impedir cópia para evitar instâncias na stack via cópia
ApenasHeap(const ApenasHeap&) = delete;
ApenasHeap& operator=(const ApenasHeap&) = delete;
};
3. Classes com Alocação Exclusiva na Stack
Para restringir a criação de objetos apenas à pilha (stack), impedindo o uso do operador new, podemos sobrecarregar os operadores de alocação de memória como privados ou simplesmente deletá-los.
class ApenasStack {
public:
ApenasStack() {}
// Deletar os operadores new e delete impede alocação no heap
void* operator new(size_t size) = delete;
void operator delete(void* ptr) = delete;
};
4. Classes Não Herdáveis (Finais)
No C++ moderno, a forma mais eifciente de impedir que uma classe seja utilizada como base para outras é através da palavra-chave final. Antes do C++11, isso era simulado tornando os construtores privados.
// Abordagem moderna com 'final'
class ClasseTerminal final {
// Implementação
};
// Tentativa de herança resultará em erro de compilação:
// class Falha : public ClasseTerminal {};
5. Implementação do Padrão Singleton
O Singleton garante que uma classe possua apenas uma instância em todo o ciclo de vida da aplicação, fornecendo um ponto global de acesso.
Abordagem Eager (Faminta)
A instância é criada no momento em que o programa é carregado, antes mesmo da execução do main. É inerentemente segura para threads, mas pode impactar o tempo de inicialização.
class SingletonEager {
public:
static SingletonEager* GetInstance() {
return &instancia;
}
private:
SingletonEager() {}
SingletonEager(const SingletonEager&) = delete;
SingletonEager& operator=(const SingletonEager&) = delete;
static SingletonEager instancia;
};
// Inicialização estática
SingletonEager SingletonEager::instancia;
Abordagem Lazy (Preguiçosa)
A instância é criada apenas quando o método de acesso é invocado pela primeira vez. Para graantir a limpeza correta da memória, pode-se utilizar uma classe interna auxiliar (Garbage Colllector).
class SingletonLazy {
public:
static SingletonLazy* GetInstance() {
if (ptr_instancia == nullptr) {
ptr_instancia = new SingletonLazy();
}
return ptr_instancia;
}
// Classe interna para desalocação automática
class Coletor {
public:
~Coletor() {
if (SingletonLazy::ptr_instancia) {
delete SingletonLazy::ptr_instancia;
SingletonLazy::ptr_instancia = nullptr;
}
}
};
private:
SingletonLazy() {}
SingletonLazy(const SingletonLazy&) = delete;
SingletonLazy& operator=(const SingletonLazy&) = delete;
static SingletonLazy* ptr_instancia;
static Coletor coletor;
};
// Inicialização de membros estáticos
SingletonLazy* SingletonLazy::ptr_instancia = nullptr;
SingletonLazy::Coletor SingletonLazy::coletor;