古典的な仮想継承のダイアモンド階層を考えてみましょう。そのような階層でのコピーとスワップのイディオムの正しい実装は何であるかを知りたいと思います。
この例は、A、B、Dクラスのデフォルトのコピーセマンティクスでうまく機能するため、少し人工的であり、あまりスマートではありません。ただし、問題を説明するためだけに、弱点の例を忘れて解決策を提供してください。
したがって、2つの基本クラス(B <1>、B <2>)から派生したクラスDがあります。各Bクラスは、実質的にAクラスから継承します。各クラスには、コピーとスワップのイディオムを使用した、自明ではないコピーセマンティクスがあります。最も派生したDクラスには、このイディオムの使用に問題があります。B<1>およびB<2>スワップメソッドを呼び出すと(仮想基本クラスメンバーを2回スワップします)、サブオブジェクトは変更されません!!!
A:
class A {
public:
A(const char* s) : s(s) {}
A(const A& o) : s(o.s) {}
A& operator = (A o)
{
swap(o);
return *this;
}
virtual ~A() {}
void swap(A& o)
{
s.swap(o.s);
}
friend std::ostream& operator << (std::ostream& os, const A& a) { return os << a.s; }
private:
S s;
};
B
template <int N>
class B : public virtual A {
public:
B(const char* sA, const char* s) : A(sA), s(s) {}
B(const B& o) : A(o), s(o.s) {}
B& operator = (B o)
{
swap(o);
return *this;
}
virtual ~B() {}
void swap(B& o)
{
A::swap(o);
s.swap(o.s);
}
friend std::ostream& operator << (std::ostream& os, const B& b)
{ return os << (const A&)b << ',' << b.s; }
private:
S s;
};
D:
class D : public B<1>, public B<2> {
public:
D(const char* sA, const char* sB1, const char* sB2, const char* s)
: A(sA), B<1>(sA, sB1), B<2>(sA, sB2), s(s)
{}
D(const D& o) : A(o), B<1>(o), B<2>(o), s(o.s) {}
D& operator = (D o)
{
swap(o);
return *this;
}
virtual ~D() {}
void swap(D& o)
{
B<1>::swap(o); // calls A::swap(o); A::s changed to o.s
B<2>::swap(o); // calls A::swap(o); A::s returned to original value...
s.swap(o.s);
}
friend std::ostream& operator << (std::ostream& os, const D& d)
{
// prints A::s twice...
return os
<< (const B<1>&)d << ','
<< (const B<2>&)d << ','
<< d.s;
}
private:
S s;
};
S
文字列を格納する単なるクラスです。
コピーを実行すると、A::sは変更されないままになります。
int main() {
D x("ax", "b1x", "b2x", "x");
D y("ay", "b1y", "b2y", "y");
std::cout << x << "\n" << y << "\n";
x = y;
std::cout << x << "\n" << y << "\n";
}
そして結果は次のとおりです。
ax,b1x,ax,b2x,x
ay,b1y,ay,b2y,y
ax,b1y,ax,b2y,y
ay,b1y,ay,b2y,y
おそらく追加B<N>::swapOnlyMe
すると問題が解決します:
void B<N>::swapOnlyMe(B<N>& b) { std::swap(s, b.s); }
void D::swap(D& d) { A::swap(d); B<1>::swapOnlyMe((B<1>&)d); B<2>::swapOnlyMe((B<2>&)d); ... }
しかし、BがAから個人的に継承する場合はどうなりますか?