Réponse courte: Oui, vous aurez besoin de répéter le travail D
Réponse longue:
Si votre classe dérivée de 'D' ne contient aucun nouveau membre variables, alors la valeur par défaut des versions (généré par le compilateur devrait fonctionner très bien). Le constructeur de Copie par défaut appelez le parent constructeur de copie et l'opérateur d'affectation par défaut appelez le parent opérateur d'affectation.
Mais si votre classe 'D' qui contient des ressources alors vous aurez besoin de faire un peu de travail.
Je trouve votre constructeur de copie un peu étrange:
B(const B& b){(*this) = b;}
D(const D& d){(*this) = d;}
Normalement copie constructeurs chaîne de sorte qu'ils sont la copie construit à partir de la base. Ici parce que vous êtes à l'appel de l'opérateur d'affectation le constructeur de copie doit appeler le constructeur par défaut à défaut d'initialiser l'objet de bas en haut en premier. Puis vous descendez à nouveau à l'aide de l'opérateur d'affectation. Cela semble plutôt inefficace.
Maintenant, si vous faites une cession de la copie de bas en haut (ou de haut en bas), mais il semble difficile pour vous de le faire et de fournir une solide exception de garantie. Si, à tout point d'une ressource ne parvient pas à copier et à vous lever une exception, l'objet est dans un état indéterminé (ce qui est une mauvaise chose).
Normalement, je l'ai vu faire dans l'autre sens.
L'opérateur d'affectation est définie par le constructeur de copie et l'échange. C'est parce qu'il est plus facile de fournir la forte exception de garantie. Je ne pense pas que vous serez en mesure de fournir la forte garantie en le faisant de cette manière autour (je peux me tromper).
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 */
}
};
Même si vous dérivez une classe D à partir de X cela n'affecte pas ce modèle.
Certes, vous avez besoin de répéter un peu de travail en faisant des appels explicites dans la classe de base, mais c'est relativement 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 */
}
};