Pregunta

Esto no es un duplicado de Implementación del constructor de copia en términos de operador = pero es una pregunta más específica. (O al menos eso me gusta pensar.)

Intro

Dado un (hipotética) clase como esta:

struct FooBar {
  long id;
  double valX;
  double valZ;
  long   valN;
  bool   flag; 
  NonCopyable implementation_detail; // cannot and must not be copied

  // ...
};

que no puede copiar este por las funciones por defecto generado, ya que no puede copiar constructo ni copiar un objeto noncopyable. Sin embargo, esta parte del objeto es un detalle de implementación que en realidad no están interesados ??en la copia.

Lo hace también no tiene ningún sentido escribir una función de intercambio para este , debido a que la función de intercambio solo podía replicar lo std :: swap de hace (menos el noncopyable).

Así que si queremos copiar estos objetos, nos quedamos con la implementación de la copia-ctor y de nosotros mismos copia-operador. Esto se hace trivialmente por la asignación a los otros miembros.

Pregunta

Si tenemos que aplicar ctor copia y el operador, debemos poner en práctica el ctor copia en términos del operador de copia, o deberíamos "duplicar" el código de inicialización con la lista?

Esto es, dado:

FooBar& operator=(FooBar const& rhs) {
  // no self assignment check necessary
  id = rhs.id;
  valX = rhs.valX;
  valZ = rhs.valZ;
  valN = rhs.valN;
  flag = rhs.flag;
  // don't copy implementation_detail
  return *this;
}

¿Hay que escribir)

FooBar(FooBar const& rhs) {
  *this = rhs;
}

o b)

FooBar(FooBar const& rhs)
: id(rhs.id)
, valX(rhs.valX)
, valZ(rhs.valZ)
, valN(rhs.valN)
, flag(rhs.flag)
// don't copy implementation_detail
{ }

aspectos posibles para una respuesta sería el rendimiento vs. mantenimiento vs. facilidad de lectura.

¿Fue útil?

Solución

En general, yo prefiero b) a través de una), ya que evita explícitamente cualquier tipo de construcción por defecto de los miembros. Para enteros, dobles, etc., que no es una consideración, pero puede ser para los miembros con operaciones costosas o efectos secundarios. Es más fácil de mantener si no se tiene en cuenta este potencial costo / problema ya que va a añadir y eliminar miembros. Initialiser listas también soportan referencias y elementos no predeterminados-construible.

Como alternativa, podría tener una sub-estructura para los miembros que no sean "detalle de implementación" y dejar que el compilador genera código copiado, a lo largo de las líneas:

struct X 
{
    struct I
    {
        int x_;
        int y_;
    } i_;
    int z_;

    X() { }

    X(const X& rhs)
      : i_(rhs.i_), z_(0) // implementation not copied
    { }

    X& operator=(const X& rhs)
    {
        i_ = rhs.i_;
        return *this;
    } 
};

Otros consejos

Normalmente implementar operador de asignación en función de constructor de copia (versión de @Roger Pate):

FooBar& operator=(FooBar copy) { swap(*this, copy); return *this; }
friend void swap(FooBar &a, FooBar &b) {/*...*/}

Esto requiere proporcionar una función swap la que intercambia los miembros relevantes (salvo implementation_detail en su caso).

Si no lanza swap Este enfoque garantiza que el objeto no se deja en un estado incoherente (con sólo los miembros de pieza asignado).

Sin embargo, en su caso, ya que ni constructor de copia, ni operador de asignación puede lanzar la aplicación constructor de copia en términos de operador de asignación (A) también está muy bien y es más fácil de mantener y luego tener código casi idéntica en ambos lugares (b).

Si usted está realmente molesta acerca de la replicación std :: swap, por qué no poner todo lo que no sea el detalle de implementación en una estructura?

struct FooBarCore {
  long id;
  double valX;
  double valZ;
  long   valN;
  bool   flag; 
  // ...
};

struct FooBar {
  FooBarCore core_;
  NonCopyable implementation_detail; // cannot and must not be copied
};

A continuación, se puede utilizar std :: intercambio para esta estructura en su función de copia de FooBar.

FooBar& operator=(const FooBar &src) {
  FooBarCore temp(src.core_)
  swap(temp,*this.core_);
  return *this;
}

Está bien, otro intento, basado en mi comentario a esta respuesta .

Envolver el implementation_detail en una clase copiable:

class ImplementationDetail
{
public:
  ImplementationDetail() {}
  ImplementationDetail(const ImplementationDetail&) {}
  ImplementationDetail& operator=(const ImplementationDetail&) {}

public: // To make the example short
  Uncopyable implementation_detail;
};

y el uso de esta clase en su FooBar. El valor predeterminado generado constructor de copia y copiar Asignación de operadora para Foobar funcionará correctamente.

Tal vez incluso podría derivar de incopiable por lo que no consigue implementation_detail.implementation_detail todo su código. O si controlas el código para la clase implementation_detail, sólo tiene que añadir el vacío constructor de copia y asignación vacío operador.

Si el constructor de copia no necesita copiar implementation_detail y todavía será correcta (dudo que lo segundo, pero vamos a suponer que por el momento), el implementation_detail es superflua.

Así que la solución parece ser:. Hacer que la estática implementation_detail y se basan en el valor predeterminado generado constructor de copia y Asignación de operadora

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top