Desactivar constructor de copia

173

Tengo una clase :

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

public:
  static inline SymbolIndexer & GetUniqueInstance ( ) 
  { 
    static SymbolIndexer uniqueinstance_ ;
    return uniqueinstance_ ; 
  }
};

¿Cómo debo modificarlo para deshabilitar código como:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

y solo permite códigos como:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );
Humilde depurador
fuente
1
Por cierto, ¿es este un singleton con disposiciones para la herencia (dado protegido)?
R. Martinho Fernandes
Tengo una duda sobre su código cada vez que se cree una instancia diferente, creo que GetUniqueInstance () siempre dará referencia al mismo objeto.
Pratham Shah

Respuestas:

286

Puede hacer que el constructor de copia sea privado y no proporcionar implementación:

private:
    SymbolIndexer(const SymbolIndexer&);

O en C ++ 11, prohíba explícitamente:

SymbolIndexer(const SymbolIndexer&) = delete;
R. Martinho Fernandes
fuente
43
En cuanto a la deletepalabra clave, me gustaría agregar lo siguiente. Mi hábito actual al diseñar una nueva clase es para deleteel constructor de copias y el operador de asignación de inmediato. Descubrí que, según el contexto, son en su mayoría innecesarios y eliminarlos evita algunos casos de comportamiento inesperado. Si se produce una situación en la que puede ser necesario un copiador, determine si se puede hacer con la semántica de movimiento. Si esto no es deseable, proporcione una implementación para ambos (!) El copiador y el operador de asignación. Si este es un buen enfoque, lo dejaré en manos del lector.
pauluss86 01 de
1
@ pauluss86 Me gusta su enfoque, pero no me comprometería totalmente con él, ya que creo que el tiempo dedicado a seguir este patrón es mayor que el tiempo ahorrado por los errores que previene. Simplemente prohíbo la copia cuando no estoy seguro.
Tomáš Zato - Restablece a Mónica el
@ pauluss86 Esto es básicamente lo que hace Rust: Move-by-default (y const-by-default). Muy útil en mi opinión.
Kapichu
33

Si no le importa la herencia múltiple (después de todo, no es tan malo), puede escribir una clase simple con un constructor de copia privada y un operador de asignación y, además, subclasificarla:

class NonAssignable {
private:
    NonAssignable(NonAssignable const&);
    NonAssignable& operator=(NonAssignable const&);
public:
    NonAssignable() {}
};

class SymbolIndexer: public Indexer, public NonAssignable {
};

Para GCC esto da el siguiente mensaje de error:

test.h: In copy constructor ‘SymbolIndexer::SymbolIndexer(const SymbolIndexer&)’:
test.h: error: ‘NonAssignable::NonAssignable(const NonAssignable&)’ is private

Sin embargo, no estoy muy seguro de que esto funcione en todos los compiladores. Hay una pregunta relacionada , pero aún sin respuesta.

UPD:

En C ++ 11 también puede escribir la NonAssignableclase de la siguiente manera:

class NonAssignable {
public:
    NonAssignable(NonAssignable const&) = delete;
    NonAssignable& operator=(NonAssignable const&) = delete;
    NonAssignable() {}
};

La deletepalabra clave evita que los miembros se construyan por defecto, por lo que no se pueden usar más en los miembros construidos por defecto de una clase derivada. Intentar asignar da el siguiente error en GCC:

test.cpp: error: use of deleted function
          ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
test.cpp: note: ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
          is implicitly deleted because the default definition would
          be ill-formed:

UPD:

Boost ya tiene una clase solo para el mismo propósito, supongo que incluso se implementa de manera similar. La clase se llama boost::noncopyabley está destinada a usarse como se indica a continuación:

#include <boost/core/noncopyable.hpp>

class SymbolIndexer: public Indexer, private boost::noncopyable {
};

Recomiendo apegarse a la solución de Boost si la política de su proyecto lo permite. Consulte también otra boost::noncopyablepregunta relacionada para obtener más información.

firegurafiku
fuente
¿No debería ser eso NonAssignable(const NonAssignable &other);?
Troyseph
Creo que esta pregunta obtendría más votos positivos si se actualizara a la deletesintaxis de palabras clave de C ++ 11 .
Tomáš Zato - Restablece a Mónica el
@ TomášZato: La idea es mantener el constructor de copias y el operador de asignación presentes, pero privados. Si usted delete, deja de funcionar (lo acabo de comprobar).
firegurafiku
@ TomášZato: Ah, lo siento, mi método de prueba estaba un poco equivocado. Eliminar también funciona. Actualizará la respuesta en un minuto.
firegurafiku
3
@Troyseph: const Class&y Class const&son bastante iguales. Para los punteros, puede tener incluso el Class const * consttipo.
firegurafiku
4

Hacer SymbolIndexer( const SymbolIndexer& )privado Si está asignando a una referencia, no está copiando.

Aaron Klotz
fuente