簡単な回答: はい、D で作業を繰り返す必要があります。
長い答え:
派生クラス 'D' に新しいメンバー変数が含まれていない場合は、既定のバージョン (コンパイラによって生成されたもの) が問題なく動作するはずです。デフォルトの Copy コンストラクターは親のコピー コンストラクターを呼び出し、デフォルトの代入演算子は親の代入演算子を呼び出します。
ただし、クラス「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 */
}
};
X からクラス D を派生させたとしても、これはこのパターンには影響しません。
確かに、基本クラスに明示的な呼び出しを行うことで少し作業を繰り返す必要がありますが、これは比較的簡単です。
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 */
}
};