Краткий ответ: Да, вам нужно будет повторить работу в D
Длинный ответ:
Если ваш производный класс 'D' не содержит новых переменных-членов, то версии по умолчанию (сгенерированные компилятором должны работать нормально). Конструктор копирования по умолчанию вызовет конструктор родительского копирования, а оператор присваивания по умолчанию вызовет родительский оператор присваивания.
Но если ваш класс 'D' содержит ресурсы, вам нужно будет поработать.
Я считаю ваш конструктор копирования немного странным:
B(const B& b){(*this) = b;}
D(const D& d){(*this) = d;}
Обычно копируют цепочку конструкторов так, чтобы они копировались с нуля. Здесь, поскольку вы вызываете оператор присваивания, конструктор копирования должен вызвать конструктор по умолчанию, чтобы по умолчанию инициализировать объект снизу вверх. Затем вы снова идете вниз, используя оператор присваивания. Это кажется довольно неэффективным.
Теперь, если вы выполняете задание, которое копируете снизу вверх (или сверху вниз), вам кажется, что это трудно сделать и предоставить надежную гарантию исключения. Если в какой-то момент ресурс не удается скопировать, и вы генерируете исключение, объект будет в неопределенном состоянии (что плохо).
Обычно я видел, как все было наоборот.
Оператор присваивания определяется в терминах конструктора копирования и обмена. Это потому, что это упрощает предоставление строгой гарантии исключения. Я не думаю, что вы сможете обеспечить надежную гарантию, если сделаете это так (я могу ошибаться).
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 */
}
};
Даже если вы наследуете класс D из X, это не влияет на этот шаблон.
По общему признанию вам нужно повторить немного работы, сделав явные вызовы в базовый класс, но это относительно тривиально.
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 */
}
};