Respuesta corta: Sí tendrá que repetir el trabajo en D
Respuesta larga:
Si su clase 'D' derivado no contiene nuevas variables miembro entonces las versiones predeterminadas (generados por el compilador debe funciona bien). El constructor de copia predeterminado llamará al constructor de copia padre y el operador de asignación predeterminado llamará al operador de asignación primaria.
Pero si su clase 'D' contiene recursos, entonces tendrá que trabajar.
Encuentro su constructor de copia un poco extraño:
B(const B& b){(*this) = b;}
D(const D& d){(*this) = d;}
copia Normalmente cadena de constructores para que se construyen a partir de copia de la base hacia arriba. Aquí, como llama al operador de asignación, el constructor de copia debe llamar al constructor predeterminado para inicializar el objeto de abajo hacia arriba. Luego vuelves a bajar usando el operador de asignación. Esto parece bastante ineficiente.
Ahora, si realiza una tarea, está copiando de abajo hacia arriba (o de arriba hacia abajo), pero le parece difícil hacerlo y proporciona una fuerte garantía de excepción. Si en algún momento un recurso no puede copiar y usted lanza una excepción, el objeto estará en un estado indeterminado (lo cual es algo malo).
Normalmente lo he visto hecho al revés.
El operador de asignación se define en términos del constructor de copia y el intercambio. Esto es porque facilita la garantía de excepción fuerte. No creo que pueda proporcionar la garantía sólida al hacerlo de esta manera (podría estar equivocado).
class X
{
// If your class has no resources then use the default version.
// Dynamically allocated memory is a resource.
// If any members have a constructor that throws then you will need to
// write your owen version of these to make it exception safe.
X(X const& copy)
// Do most of the work here in the initializer list
{ /* Do some Work Here */}
X& operator=(X const& copy)
{
X tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(X& s) throws()
{
/* Swap all members */
}
};
Incluso si obtiene una clase D desde X, esto no afecta a este patrón.
Es cierto que debe repetir un poco del trabajo haciendo llamadas explícitas a la clase base, pero esto es relativamente trivial.
class D: public X
{
// Note:
// If D contains no members and only a new version of foo()
// Then the default version of these will work fine.
D(D const& copy)
:X(copy) // Chain X's copy constructor
// Do most of D's work here in the initializer list
{ /* More here */}
D& operator=(D const& copy)
{
D tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(D& s) throws()
{
X::swap(s); // swap the base class members
/* Swap all D members */
}
};
Si desea sobrescribir el método 'foo', puede usar' using B :: operator =; 'para heredar el operador de asignación, pero los constructores de copia y movimiento no se pueden heredar: https://stackoverflow.com/q/49045026/5447906 –