3

我有如下情况:

class A {
    virtual void f() { /*some default impl here*/}
    virtual void g() { /*some default impl here*/}
};

class B : public A {
    virtual void f() { /* do some specific stuff and call parent's f()*/}
    virtual void g() { /* do some specific stuff and call parent's g()*/}
};

class C : public A {
    virtual void f() { /* do some specific stuff and call parent's f()*/}
    virtual void g() { /* do some specific stuff and call parent's g()*/}
};

class mixed /* I don't know how to derive*/ {
    virtual void f() { /* call B::f()*/}
    virtual void g() { /* call C::g()*/}
};

我在这里考虑多重继承。即, makemixed派生自BC。但是有一些已知的问题(例如, 钻石问题)。

另一种解决方案可能是组合。

但是什么是正确的解决方案,请指教:)

提前致谢!

4

4 回答 4

4

一些已知问题(例如,钻石问题)。

第一:除非您明确创建菱形图案,否则没有菱形图案。

 class mixed: public B, public C

这将使 B 和 C 混合继承。每个都有自己的显式 A(没有菱形)。

由于 B 和 C 都具有从 A 派生的虚拟成员,因此应该调用哪个变得模棱两可,但是您已经通过明确定义所有虚拟成员来解决这个mixed问题(因此问题解决了)。

  void mixed::f() { B::f();} // works fine.

现在,即使您明确地创建了钻石。

注意:菱形图案不会正常出现。菱形图案是您必须明确做出的设计决策,您可以使用它来解决某些类型的问题(通过使用虚拟继承)。

class B: public virtual A ...
class C: public virtual A ...
class mixed: public B, public C ...

你仍然没有问题。因为mixed::f()只使用B分支(然后是 A)。虽然mixed::g()只使用C分支(然后是 A)。

即使A有自己的状态(尽管这可能是个坏主意,通常最好将接口用作虚拟基类),那么我们也没有问题,因为mixed::f()并且mixed::g()只在一个孩子中调用一个函数(如果他们打电话给双方,A两个电话的状态都发生了变化。

另一种解决方案可能是组合。

那也行。

 class mixed: public A
 {
     B    b;
     C    c;
     public:
         virtual void f() override {b.f();}
         virtual void g() override {c.g();}
     ....
 };

但是什么是正确的解决方案

没有正确的解决方案。这在很大程度上取决于您没有提到的细节(比如 A 的细节是什么)。

但是一般建议是更喜欢组合而不是继承,但这只是一般建议细节总是归结为要解决的实际问题。

于 2015-07-14T14:26:11.873 回答
3

每个方法在调用父方法之前都必须“做一些事情”这一事实导致了问题。

一种解决方案是让班级A成员B成为mixed班级成员。然后你可以控制你需要用它们做mixed::f()什么mixed::g()

如果需要,您可以创建一个base带有纯虚函数的基类f(),并且g(). mixed可以继承, 和 也A可以B继承C。你在讨论作文时暗示了这种可能性。

于 2015-07-14T14:12:10.350 回答
3

我想你可能正在寻找这样的东西。(对不起,这是一大堆代码,但它真的很简单。)

#include <iostream>

struct A
{
  virtual void
  f()
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
  }

  virtual void
  g()
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
  }

  // Don't forget the virtual destructor.
  virtual ~A() noexcept = default;
};

struct B : virtual A
{
  virtual void
  f() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    A::f();
  }

  virtual void
  g() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    A::g();
  }
};

struct C : virtual A
{
  virtual void
  f() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    A::f();
  }

  virtual void
  g() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    A::g();
  }
};

struct D : virtual B, virtual C
{
  virtual void
  f() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    B::f();
  }

  virtual void
  g() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    C::g();
  }
};

int
main()
{
  D d {};
  d.f();
  std::cout << '\n';
  d.g();
}

override是 C++11 的一项功能,可以让编译器检查您实际覆盖的内容。使用它是一种很好的做法,但如果您的编译器不支持它,则不需要它。 __PRETTY_FUNCTION__是一个 GCC 扩展,用于获取命名当前函数签名的字符串文字。标准 C++ 有__func__,但在这里用处不大。如果您的编译器没有与__PRETTY_FUNCTION__.

输出:

virtual void D::f()
virtual void B::f()
virtual void A::f()

virtual void D::g()
virtual void C::g()
virtual void A::g()

它有效,但我不认为这个漂亮的代码。组合可能是这里更好的解决方案。

于 2015-07-14T14:22:12.930 回答
1

这是虚拟继承的替代方案:使用CRTP将功能混合到BCM,同时共享一个公共的A而没有vtables 的开销。

#include <iostream>

struct A
{
    int a;
};

template <typename T>
struct B
{
    A *get_base_a() {return static_cast<T*>(this);}
    void print_a() {std::cout << get_base_a()->a << '\n';}
};

template <typename T>
struct C
{
    A *get_base_a() {return static_cast<T*>(this);}
    void print_a() {std::cout << get_base_a()->a << '\n';}
};

struct M : A, B<M>, C<M>
{
};

int main()
{
    M m;
    m.a = 1;
    m.B::print_a();
    m.C::print_a();
    return 0;
}

但是请注意,您将无法将 an 传递M*给需要 aB*或 a的函数C*

于 2015-07-14T14:25:15.817 回答