【问题标题】:Strategy for calling functions in classes with diamond inheritance and virtual base classes在具有菱形继承和虚拟基类的类中调用函数的策略
【发布时间】:2014-01-05 23:57:20
【问题描述】:

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

#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 类中,因此我们可以访问受保护的成员。

【问题讨论】:

  • 我能想到的就是在 A::foo() 的调用周围或内部放置一些保护变量 - 对不起!
  • 除了@polkadotcadaver 的建议之外,我能想到的唯一其他方法是不要在BC 实现中调用A::foo。不过,可能不是你想要的。

标签: c++ inheritance c++11


【解决方案1】:

只是 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 的临时对象来工作的,构造函数遵循 virtual 基本名称,因此 foo::A::A() 只被调用一次。

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

【讨论】:

  • @pnuts:不用担心 - 一切都是为了提供帮助。干杯。
猜你喜欢
  • 1970-01-01
  • 2013-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-16
  • 2016-05-20
  • 1970-01-01
相关资源
最近更新 更多