在虚拟继承中交换和复制习语的正确方法是什么? [英] What is the proper approach to swap and copy idiom in virtual inheritance?

查看:198
本文介绍了在虚拟继承中交换和复制习语的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑经典的虚拟继承菱形层次结构。我想知道什么是在这种层次结构中的复制和交换惯用语的正确实现。

Consider classic virtual inheritance diamond hierarchy. I wonder to know what is the right implementation of copy and swap idiom in such hierarchy.

示例有点人为 - 它是不是很聪明 - 因为它会发挥良好的默认复制语义为A,B,D类。但只是为了说明问题 - 请忘记示例弱点并提供解决方案。

The example is a little artificial - and it is not very smart - as it would play good with default copy semantic for A,B,D classes. But just to illustrate the problem - please forget about the example weaknesses and provide the solution.

所以我有从2个基类派生的类D(B 1,B 2) - 每个B类都从A类继承。每个类具有使用复制和交换惯用法的非简单复制语义。最衍生的D类在使用这个习语时有问题。当它调用B< 1>和B< 2>交换方法 - 它交换虚拟基类成员两次 - 因此子对象保持不变!!!

So I have class D derived from 2 base classes (B<1>,B<2>) - each of B classes inherits virtually from A class. Each class has non trivial copy semantics with using of copy and swap idiom. The most derived D class has problem with using this idiom. When it calls B<1> and B<2> swap methods - it swaps virtual base class members twice - so A subobject remains unchanged!!!

p>

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 只是一个存储字符串的类。

S is just a class storing string.

复制时,您将看到A :: s保持不变:

When doing copy you will see A::s remains unchanged:

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私下继承时呢?

But what when B inherits privately from A?

推荐答案

这里是一个哲学的咆哮:

Here's a philosophical rant:


    < t认为虚拟继承可以或应该是私有的。虚拟基础的整个点是最大的派生类拥有虚拟基础,而不是中间类。因此,没有中间类应该允许虚拟虚拟基础。
  1. I don't think virtual inheritance can or should be private. The entire point of a virtual base is that the most derived class owns the virtual base, and not the intermediate classes. Thus no intermediate class should be permitted to "hog" the virtual base.

让我重复一点:最大派生类拥有虚拟基础。这在构造函数初始化器中很明显:

Let me repeat the point: The most derived class owns the virtual base. This is evident in constructor initializers:

D::D() : A(), B(), C() { }
//       ^^^^
//       D calls the virtual base constructor!

同样的道理, > D 应立即负责 A 。因此,我们自然会导致编写派生交换函数,如下所示:

In the same sense, all other operations in D should be immediately responsible for A. Thus we are naturally led to writing the derived swap function like this:

void D::swap(D & rhs)
{
    A::swap(rhs);   // D calls this directly!
    B::swap(rhs);
    C::swap(rhs);

    // swap members
}


  • 所有这一切,我们只剩下一个可能的结论:你必须编写中间类的交换函数,而不交换基础:

  • Putting all this together, we're left with only one possible conclusion: You have to write the swap functions of the intermediate classes without swapping the base:

    void B::swap(B & rhs)
    {
        // swap members only!
    }
    
    void C::swap(C & rhs)
    {
        // swap members only!
    }
    


  • 如果有人想从 D 派生?现在我们看到Scott Meyer的建议总是使非叶类抽象的原因:根据这个建议,仅实施最终的 swap 函数,该函数调用具体的叶类中的虚拟基础交换。

    Now you ask, "what if someone else wants to derive from D? Now we see the reason for Scott Meyer's advice to always make non-leaf classes abstract: Following that advice, you only implement the final swap function which calls the virtual base swap in the concrete, leaf classes.

    更新:这里只有相切的关系:虚拟交换我们继续假设所有非叶类都是抽象的首先, virtual swap functioninto each base class(virtual或not):

    Update: Here's something only tangentially related: Virtual swapping. We continue to assume that all non-leaf classes are abstract. First off, we put the following "virtual swap function" into every base class (virtual or not):

    struct A
    {
        virtual void vswap(A &) = 0;
        // ...
    };
    

    这个函数的使用当然只保留给相同类型。这由隐式异常保护:

    Usage of this function is of course reserved only to identical types. This is safeguarded by an implicit exception:

    struct D : /* inherit */
    {
        virtual void vswap(A & rhs) { swap(dynamic_cast<D &>(rhs)); }
    
        // rest as before
    };
    

    这种方法的整体效用有限,但它允许我们知道它们是相同的:

    The overall utility of this is limited, but it does allow us swap to objects polymorphically if we happen to know that they're the same:

    std::unique_ptr<A> p1 = make_unique<D>(), p2 = make_unique<D>();
    p1->vswap(*p2);
    

    这篇关于在虚拟继承中交换和复制习语的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆