c – 在具有钻石继承和虚拟基类的类中调用函数的策略

如果我们有钻石继承并使用公共虚拟基类,我们可以阻止第一个构造函数被多次调用.现在,我想对构造函数之外的函数做同样的事情.例如,代码:

#include <iostream>

struct A {
    virtual void foo() {
        std::cout << "A" << std::endl;
    }
};
struct B : virtual public A {
    virtual void foo() {
        A::foo();
        std::cout << "B" << std::endl;
    }
};
struct C : virtual public A {
    virtual void foo() {
        A::foo();
        std::cout << "C" << std::endl;
    }
};
struct D : public B, public C{
    virtual void foo() {
        B::foo();
        C::foo();
        std::cout << "D" << std::endl;
    }
};

int main() {
    D d;
    d.foo();
}

产生结果

A
B
A
C
D

我想修改它以便它只产生

A
B
C
D

什么样的策略或模式可以实现这一目标?

编辑1

我更喜欢Tony D的答案,而不是以下.尽管如此,理论上可以使用另一个类的构造函数来定义适当的函数层次结构.特别

#include <iostream>

struct A;
struct B;
struct C;
struct D;

namespace foo {
    struct A {
        A(::A* self);
    };
    struct B : virtual public A {
        B(::B* self);
    };
    struct C : virtual public A {
        C(::C* self);
    };
    struct D : public B, public C{
        D(::D* self);
    };
}

struct A {
private:
    friend class foo::A;
    friend class foo::B;
    friend class foo::C;
    friend class foo::D;
    int data;
public:
    A() : data(0) {}
    virtual void foo() {
        (foo::A(this));
    }
    void printme() {
        std::cout << data << std::endl;
    }
};
struct B : virtual public A {
    virtual void foo() {
        (foo::B(this));
    }
};
struct C : virtual public A {
    virtual void foo() {
        (foo::C(this));
    }
};
struct D : public B, public C{
    virtual void foo() {
        (foo::D(this));
    }
};

foo::A::A(::A* self) {
    self->data+=1;
    std::cout << "A" << std::endl;
}
foo::B::B(::B* self) : A(self) {
    self->data+=2;
    std::cout << "B" << std::endl;
}
foo::C::C(::C* self) : A(self) {
    self->data+=4;
    std::cout << "C" << std::endl;
}
foo::D::D(::D* self) : A(self), B(self), C(self) {
    self->data+=8;
    std::cout << "D" << std::endl;
}

int main() {
    D d;
    d.foo();
    d.printme();
}

基本上,命名空间foo中的类对名为foo的函数进行计算.这似乎有点冗长,所以也许有更好的方法来做到这一点.

编辑2

再次感谢Tony D澄清上面的例子.是的,基本上上面的做法是创建符合虚拟基本名称的临时变量.通过这种方式,我们可以使用构造函数来防止冗余计算.额外的尝试是试图展示如何访问可能已被埋在基类中的私人成员.稍微考虑一下,还有另一种方法可以做到这一点,取决于应用程序可能会更清洁也可能不会更清洁.我会留在这里供参考.与上一个例子一样,缺点是我们基本上需要手动再次连接继承.

#include <iostream>

struct A {
protected:
    int data;
public:
    A() : data(0) {}
    struct foo{
        foo(A & self) {
            self.data+=1;
            std::cout << "A" << std::endl;
        }
    };
    void printme() {
        std::cout << data << std::endl;
    }
};
struct B : virtual public A {
    struct foo : virtual public A::foo {
        foo(B & self) : A::foo(self) {
            self.data+=2;
            std::cout << "B" << std::endl;
        }
    };
};
struct C : virtual public A {
    struct foo : virtual public A::foo {
        foo(C & self) : A::foo(self) {
            self.data+=4;
            std::cout << "C" << std::endl;
        }
    };
};
struct D : public B, public C{
    struct foo : public B::foo, public C::foo {
        foo(D & self) : A::foo(self) , B::foo(self), C::foo(self) {
            self.data+=8;
            std::cout << "D" << std::endl;
        }
    };
};

int main() {
    D d;
    (D::foo(d));
    d.printme();
}

本质上,调用(D :: foo(d))创建一个临时的谁的构造函数执行我们想要的操作.我们手动传入对象d以访问内存.由于类foo位于类A..D中,因此可以访问受保护的成员.

最佳答案
只是polkadotcadaver的想法的实现.在这里,Limiter被设计为可重用的机制,虚拟基类应该具有该类型的成员.受控的基类函数使用bool Limiter :: do_next()来询问它是否应该“像往常一样”运行或立即返回,而调用基类函数的派生类从获取所有权的限制器获取范围保护对象如果尚未声明,并释放它在销毁时的所有权.

#include <iostream>

class Limiter
{
  public:
    Limiter() : state_(Unlimited) { }

    class Scope
    {
      public:
        Scope(Limiter& l)
          : p_(l.state_ == Unlimited ? &l : NULL)
        { if (p_) p_->state_ = Do_Next; }

        ~Scope() { if (p_) p_->state_ = Unlimited; }
      private:
        Limiter* p_;
    };

    Scope get() { return Scope(*this); }

    bool do_next()
    {
        if (state_ == Do_Next) { state_ = Suspended; return true; }
        return state_ != Suspended;
    }

  private:
    enum State { Unlimited, Do_Next, Suspended } state_;
};

struct A {
    Limiter limiter_;
    virtual void foo() {
        if (limiter_.do_next())
            std::cout << "A" << std::endl;
    }
};
struct B : virtual public A {
    virtual void foo() {
        Limiter::Scope ls = A::limiter_.get();
        A::foo();
        std::cout << "B" << std::endl;
    }
};
struct C : virtual public A {
    virtual void foo() {
        Limiter::Scope ls = A::limiter_.get();
        A::foo();
        std::cout << "C" << std::endl;
    }
};

struct D : public B, public C{
    virtual void foo() {
        Limiter::Scope ls = A::limiter_.get();
        B::foo();
        C::foo();
        std::cout << "D" << std::endl;
    }
};

int main() {
    D d;
    d.foo();
}

讨论技术编辑到你的问题

花了一些时间来弄清楚你在代码中做了些什么;-P – 所以为了讨论起见,我会发布我把它归结为:

#include <iostream>

namespace foo {
    struct A {
        A() { std::cout << "A\n"; }
    };
    struct B : virtual public A {
        B() { std::cout << "B\n"; }
    };
    struct C : virtual public A {
        C() { std::cout << "C\n"; }
    };
    struct D : public B, public C{
        D() { std::cout << "D\n"; }
    };
}

struct A { virtual void foo() { foo::A(); } };
struct B : virtual public A { void foo() { foo::B(); } };
struct C : virtual public A { void foo() { foo::C(); } };
struct D : public B, public C { void foo() { foo::D(); } };

int main() {
    D d;
    d.foo();
}

为了别人的缘故 – 这是通过让A..D :: foo()函数创建foo :: A..D类型的临时对象来实现的,这些构造函数为虚拟基本名称所以foo :: A :: A ()只被调用一次.

作为一般解决方案,问题在于您必须手动同步foo ::结构,因此存在冗余和脆弱性.虽然很聪明!

转载注明原文:c – 在具有钻石继承和虚拟基类的类中调用函数的策略 - 代码日志