我可以对来自不同基类的相同函数实现不同的实现吗?

时间:2013-12-22 14:57:46

标签: c++ inheritance

假设如下:

class A{ virtual void f() = 0; };
class B{ virtual void f() = 0; };

我可以以某种方式执行以下操作吗?

class C : public A, public B
{
  virtual void A::f(){ printf("f() from A"); }
  virtual void B::f(){ printf("f() from B"); }
};

所以现在我可以做了???

A* pa = new C();
pa->f(); // prints f() from A;
B* pb = (B*)pa;
pb->f(); // prints f() from B;

感谢!!!

4 个答案:

答案 0 :(得分:5)

第一个解决方案

这个问题提醒了'立面'的设计模式。 这应该重写为:

class AC : public A
{ public: virtual void f(){ cout << "f() from A" << endl;}; };

class BC : public B ...

class C : public AC, public BC {};

其中C是'立面'。

所以在正确的调用语法中应该是这样的:

C* c = new C();
c->AC::f();
c->BC::f();

如果AC和AC之间没有任何共享限制不管怎么说,这应该可以完成工作,因为它没有被玷污。

第二个解决方案

另一个解决方案,感谢Casey (参见第一条评论),是在模板中使用C类的前向声明,以允许调用方法来定义后者。

template <typename C>
class AC : public A {
public:
    void f() { static_cast<C&>(*this).f_from_A(); }
};

template <typename C>
class BC : public B { ... };

所以实现部分可以在同一个类中完成。

class C : public AC<C>, public BC<C> {
public:
    void f_from_A() { cout << "f_from_A" << endl; };
    void f_from_B() ...
};

调用部分更干净,因为它没有显示任何实现细节它最接近问题

C* c = new C();
((A*) c) -> f();
((B*) c) -> f();

C上没有'default'f(),可以打破继承的预期行为,并且更难阅读。

答案 1 :(得分:0)

供参考,以下链接似乎相关:

  1. Overloading virtual functions of the same name from different base classes. Is it possible?
  2. C++ virtual override functions with same name
  3. Derived class defines function via base class
  4. 第二个链接中的OP问他是否可以像你问的那样做:

    class Impl : public A , public B
    {
      public:
         void A::Function () {  cout << "A::Function" << endl; }
         void B::Function () {  cout << "B::Function" << endl; }
    };
    

    Johannes的回答是:

      

    您不能在那里使用限定名称。我写的void Function(){   ...}你正在重写这两个功能。 Herb Sutter展示了它是如何做到的   得到解决。

         

    另一种选择是重命名这些功能,因为显然它们   做一些不同的事情(否则我不会看到问题   用相同的行为覆盖两者。)

    因此,一个可能的解决方案(来自第一个链接的James Kanze)看起来像这样:

    class RemapA : public A
    {
        virtual void fnInA() = 0;
    public:
        virtual void fn()
        {
            fnInA();
        }
    };
    
    class RemapB : public B
    {
        virtual int fnInB() = 0;
    public:
        virtual int fn()
        {
            return fnInB();
        }
    };
    
    class C : public RemapA, public RemapB
    {
        virtual void fnInA() { /* ... */ }
        virtual void fnInB() { /* ... */ }
        //  ...
    };
    

    Herb Sutter的链接基本上也说了同样的话。

答案 2 :(得分:0)

是。每个基类都有自己的虚拟表 因此,通过强制转换,编译器将检查您要转换为的类型的虚拟表,并获取指向匹配函数的指针。

class A {public: virtual void f() = 0; };
class B {public: virtual void f() = 0; };

class C : public A, public B
{
public:
    virtual void A::f(){ printf("f() from A"); }
    virtual void B::f(){ printf("f() from B"); }
};

海湾合作委员会不支持这一点。只有Visual Studio可以。

但是你必须总是使用强制转换或范围函数调用 这将返回一个模糊的调用错误:

C* c = new C();
c->f(); //ambiguous call compilation error

This will work:
((B*)c)->f();

编译器显示如下结构:( Visual studio flag: /d1reportSingleClassLayout C

  class C   size(8):
    +---
    | +--- (base class A)
   0    | | {vfptr}
    | +---
    | +--- (base class B)
   4    | | {vfptr}
    | +---
    +---

  C::$vftable@A@:
    | &C_meta
    |  0
   0    | &C::f

  C::$vftable@B@:
    | -4
   0    | &CCCC::f

  C::f this adjustor: 4
  C::f this adjustor: 0

基本上你可以看到匹配虚拟表的2个独立指针 所以你可以这样做:

    C* c= new C();
    B* b = (B*)c;
    b->f(); // calls C::B::f()
    A* a = (A*)c;
    a->f(); // calls C::A::f()

   or even:

    a = (A*)(C*)b; 
    a->f(); // calls C::A::f()

请注意,您需要将b大小写回派生类以访问A类型基类的虚用表。

答案 3 :(得分:0)

要覆盖这两个函数,您需要中间类。 C中的using声明选择A:

的功能
#include <iostream>

struct A{ virtual void f() = 0; };
struct B{ virtual void f() = 0; };

struct IntermediateA : public A {
    virtual void f() override { std::cout << "A\n"; }
};

struct IntermediateB : public B {
    virtual void f() override { std::cout << "B\n"; }
};


struct C : public IntermediateA, public IntermediateB
{
    using IntermediateA::f;
};

int main() {
    C c;
    B* b = &c;

    c.f();
    b->f();
    return 0;
}