fuente - funciones en c++
¿Cómo hago para que este objeto de C++ no pueda copiarse? (10)
Ver titulo
Yo tengo:
class Foo {
private:
Foo();
public:
static Foo* create();
}
¿Qué debo hacer desde aquí para que Foo no pueda copiarse?
¡Gracias!
En C ++ 11, puede deshabilitar explícitamente la creación del constructor de asignación y copia predeterminado colocando = delete
después de la declaración.
De Wikipedia :
struct NonCopyable {
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable & operator=(const NonCopyable&) = delete;
};
Lo mismo ocurre con las clases, por supuesto.
Esto es lo que uso:
/* Utility classes */
struct NoCopy
{
public:
NoCopy() {}
private:
NoCopy(const NoCopy &);
};
struct NoAssign
{
private:
NoAssign &operator=(const NoAssign &);
};
struct NonInstantiable
{
private:
NonInstantiable();
};
struct NoCopyAssign : NoCopy, NoAssign
{
};
typedef NoCopyAssign NoAssignCopy;
En tu caso:
struct Example : NoCopy
{
};
Hacer el constructor de copia privado.
Foo(const Foo& src);
No necesita implementarlo, simplemente declararlo en el archivo de encabezado.
Haga que el constructor de copia y el operador de asignación también sean privados. Solo la declaración es suficiente, no tiene que proporcionar una implementación.
La buena práctica en C ++ 11 es declarar el constructor de copia y la asignación como eliminados públicamente. No eliminado de forma privada, eliminado públicamente : https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-delete
La forma típica de hacer que un objeto de C ++ no pueda copiarse es declarar explícitamente un constructor de copia y un operador de asignación de copia pero no implementarlos. Esto evitará que el compilador genere su propio. (Por lo general, esto se hace junto con declararlos private
para que genere un error de compilación en lugar de un error del vinculador).
También está la clase boost::noncopyable
la que puedes heredar, que hace lo que describí anteriormente.
Para añadir un poco allí.
La solución tradicional es, como se ha dicho, declarar que tanto Copy Constructor
como Assignment Operator
son private
, y no definirlos .
- Debido a que son
private
, esto provocará un error en el tiempo de compilación de cualquiera que intente usarlos y que no tenga acceso a las partes privadas de la clase ... - Lo que deja a los amigos (y a la clase en sí) por los cuales se producirá el error bajo la forma de
undefined symbol
, ya sea en el momento del enlace (si los verifica) o muy probablemente en el tiempo de ejecución (cuando se intenta cargar la biblioteca) .
Por supuesto, es bastante molesto en el segundo caso porque luego tiene que verificar su código usted mismo ya que no tiene la indicación del archivo y la línea en la que se produce el error. Afortunadamente está limitado a tus métodos de clase y amigos.
Además, vale la pena señalar que estas propiedades son transitivas en la ruta de la herencia y la composición: el compilador solo generará versiones Default Constructor
, el Copy Constructor
, el Assignment Operator
y el Destructor
si es posible.
Esto significa que para cualquiera de esos cuatro, se generan automáticamente solo si son accesibles para todas las bases y atributos de la clase.
// What does boost::noncopyable looks like >
class Uncopyable {
public:
Uncopyable() {}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};
Esta es la razón por la que la herencia de esta clase (o su uso como un atributo) efectivamente impide que su propia clase pueda copiarse o asignarse a menos que usted mismo defina esos operadores.
En general, la herencia se elige sobre la composición por 2 razones:
- El objeto es efectivamente
Uncopyable
, incluso si el polimorfismo puede no ser tan útil - La herencia conduce a
EBO
o a laEmpty Base Optimization
, mientras que un atributo será direccionable y, por lo tanto, ocupará memoria (en cada instancia de la clase) incluso si en realidad no lo necesita, el compilador tiene la posibilidad de no agregar esta sobrecarga para una base clase.
Como alternativa, podría declarar a los operadores como privados y no definirlos en su propia clase, pero el código sería menos autodocumentado y no podría buscar automáticamente esa clase que tenga esta propiedad (a menos que tenga un analizador en toda regla).
Espero que esto arroje algo de luz sobre el mecanismo.
Solo otra manera de rechazar el constructor de copias, por conveniencia, se puede usar una macro DISALLOW_COPY_AND_ASSIGN:
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) /
TypeName(const TypeName&) = delete; /
void operator=(const TypeName&) = delete
Luego, en clase Foo:
class Foo {
public:
Foo(int f);
~Foo();
private:
DISALLOW_COPY_AND_ASSIGN(Foo);
};
#include <boost/utility.hpp>
class Foo : boost::noncopyable {...
Pero como Scott Meyers dijo una vez: "Es una buena clase, es solo que encuentro el nombre un poco errado, no natural", o algo así.
class Foo {
private:
Foo();
Foo( const Foo& ); // non construction-copyable
Foo& operator=( const Foo& ); // non copyable
public:
static Foo* create();
}
Si está usando boost, también puede heredar de no copiable: http://www.boost.org/doc/libs/1_41_0/boost/noncopyable.hpp
EDIT: versión C ++ 11 si tiene un compilador que soporta esta característica:
class Foo {
private:
Foo();
Foo( const Foo& ) = delete; // non construction-copyable
Foo& operator=( const Foo& ) = delete; // non copyable
public:
static Foo* create();
}