具有固定实现的虚函数不使用大多数派生类(* this)

时间:2011-05-15 20:31:14

标签: c++ virtual

假设我有以下代码:

struct Z;

struct A
{
  virtual void Do (Z & z) const;
};

struct B : public A {};

struct Z
{
  void use (A const & a) {}
  void use (B const & b) {}
};


void A::Do(Z& z) const{
  z.use(*this);
}

现在,当我致电B.do时,this的类型为A,这是有意义的,因为do的实施在A中定义}。

有没有办法调用B.do使用use (B const &)而无需将do的相同代码从A复制粘贴到B?在我的实际代码中,我有大约15个(并且正在增长的)派生自某个基类的类,并且每次都需要复制粘贴do的相同代码。

[编辑] 澄清:所有Do所做的就是致电use,没有别的。 Douse是接受&访问Visitor pattern中的函数。

4 个答案:

答案 0 :(得分:3)

既然你现在澄清了你想要的是访客模式,那么,对不起,但就是这样。 This answer显示了具有双重调度的访问者模式的工作原理。


我想到了一种使用CRTP的好方法,但根据具体情况,这可能适用于您,也可能不适合您 (注意:我使用了链接答案中的代码,因此名称不匹配,但我希望你能得到这个想法。)

// your Z
class Visitor;

// superclass needed for generic handling
struct Superbase{
  virtual void Accept(Visitor& v) = 0;
};

// your A
template<class Der>
class Base : public Superbase{
public:
    void Accept(Visitor& v){
        v.Visit(static_cast<Der&>(*this));
    }
};

// your B
class Derived1 : public Base<Derived1> {
};

// new C
class Derived2 : public Base<Derived1> {
};

class Visitor {
public:
    virtual void Visit(Superbase& sup){
      // generic handling of any Superbase-derived type
    }

    virtual void Visit(Derived1& d1){
      // handle Derived1
    }

    virtual void Visit(Derived2& d2){
      // handle Derived1
    }
};

int main(){
    Visitor v;
    Derived1 d1;
    d1.Accept(v);
}

唯一的问题:现在你错过了为任何类型A提供通用句柄的机会,因为函数不能同时是虚拟和模板。 :|
刮掉它,找到一个使用Superbase基类的解决方案。 :)这甚至允许你有一个Superbase的容器,并充分利用多态性。 :)

答案 1 :(得分:2)

我认为这段代码符合您的要求:

#include <iostream>

struct A;
struct B;

struct Z
{
    void use (A const & a);
    void use (B const & b);
};

template<typename DERIVED>
struct XX
{
    void Do(Z& z){
        Do(z,THIS());
    }
private:
    const DERIVED& THIS() const { return static_cast<const DERIVED&>(*this); }
    void Do(Z& z, const DERIVED& t){
        z.use(t);
    }
};

struct A : public XX<A> {};
struct B : public XX<B> {};

void Z::use (A const & a) { std::cout << "use for A" << std::endl; }
void Z::use (B const & b) { std::cout << "use for B" << std::endl; }

int main(){

    A a;
    B b;
    Z z;
    a.Do(z);
    b.Do(z);
    return 0;
}

代码中唯一的“维护”或“样板”部分是从您自己类型模板化的模板类派生而来。

答案 2 :(得分:0)

您需要根据use指向的类型调度this的调用,因此您需要将另一个虚拟函数添加到AB,只需调用正确的use。我假设do除了调用use之外还做其他事情,否则你确实必须在每个子类中重新实现do。它看起来像这样

struct A
{
  virtual void Do (Z & z) const
  {
    // do stuff

    use(z);

    // do more stuff
  }

  virtual void use(Z & z) const
  {
    z.use(*this);
  }
};

struct B : public A
{
  virtual void use(Z & z) const
  {
    z.use(*this);
  }
};

struct Z
{
  void use (A const & a) {}
  void use (B const & b) {}
};

答案 3 :(得分:0)

我想我必须让你失望并拒绝。这是您必须做出的权衡,以便您将类的界面分解为访问者。访问者必须知道哪个人向其报告,只要您不覆盖基类中的虚拟Do(),访问者就会将您视为A.

请有人证明我错了! (我也看到这解决了删除冗余)