【问题标题】:C++ multiple inheritance from different abstract bases with same pure virtual method具有相同纯虚方法的不同抽象基的 C++ 多重继承
【发布时间】:2016-11-24 09:25:44
【问题描述】:

想象一下

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

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

class C : public A,B
{
  public: virtual void f()
  { // implementation here
  }
};

C c();

这似乎可以编译。 (没有尝试编译我的示例,而是一个更复杂的现实生活场景)。

我有点惊讶它确实可以编译。 我本来会抱怨(关于 C 的实例化),因为我只实现了一种抽象基础方法。 (即使他们有相同的名字。)

这是否像我期望的那样工作(假设函数 f 在两个基中具有相同的含义)还是有任何陷阱?

【问题讨论】:

标签: c++


【解决方案1】:

most vexing parse 又来了(你打开编译器警告了吗?):

C c();

是一个函数声明,因此没有构造任何对象。无论如何,如果您将c 声明为

C c;

因为这两个纯虚函数将被派生最多的C::f() 覆盖。

如果你要写类似的东西,就会出现歧义

class A
{
  public: void f() {}
};

class B
{
  public: void f() {}
};

class C : public A,B
{
public: 


};

int main() {

    C c;
    c.f(); // Need to call a base one, but which one?

}

错误:在不同类型的多个基类中发现成员“f”

【讨论】:

    【解决方案2】:
    C c();
    

    是一个函数声明,而不是类C的对象的创建。

    并且C c; 仍将编译,因为您的 C 类的两个虚函数 f,(从每个 AB 继承)。被单个函数 C::f() 覆盖。

    【讨论】:

      【解决方案3】:

      假设 C c(); 被放置在 main 中,那么代码可以编译是对的。

      由于两个基类中的方法完全相同,因此它们被 C 类中的定义覆盖。

      我能想到的一个陷阱是经典的钻石问题。它可能发生在 A 和 B 是同一基类的子类的情况下,其中 void f() 将被实现并且之后不会被覆盖。例如:

      class Base
      {
        public: void f() {/*do something*/}
      };
      class A : public Base {};
      
      class B : public Base {};
      
      class C : public A, public B {};
      

      在这种情况下,如果您只在代码中创建对象实例,则一切正常,直到您尝试调用该方法:

      C c;
      c.f(); // compiler error
      

      然后您将收到一条关于对f 成员的模糊调用的错误消息。

      但也有解决方案(虚拟继承

      您可以阅读有关该主题的更多信息,例如 here

      【讨论】:

        【解决方案4】:

        没有人提到您仍然可以完全符合会员资格:

        #include <iostream>
        
        class A {
          public: virtual void f() {  
            std::cout << "A" << std::endl;
          }
        };
        
        class B {
          public: virtual void f() {  
            std::cout << "B" << std::endl;
          }
        };
        
        class C : public A, public B { 
          public: virtual void f() {  
            std::cout << "C" << std::endl;
          }
        };
        
        int main() {
            C c;
            c.A::f();
            c.B::f(); 
            c.f();
        }
        

        输出

        A
        B
        C
        

        【讨论】:

          【解决方案5】:

          OP 说他们只实现了其中一个抽象函数,但事实并非如此。如果类C 实现f(),那么AB 现在都有具体的实现。更重要的是,C 真正多态于AB

          class A
          {
            public: virtual void f() = 0;
          };
          
          class B
          {
            public: virtual void f() = 0;
          };
          
          class C : public A,B
          {
            public: virtual void f()
            { // implementation here
            }
          };
          
          C c;
          c.f();   // works
          A& a(c);
          a.f();   // works
          B& b(c);
          b.f();   // works
          
          A* aa = &c;
          aa->f();   // works
          B* bb = &c;
          bb->f();   // works
          

          要意识到的重要一点是aabb(指针)的数值相等,即使它们指向同一个对象c。当cc 被强制转换为AB 的指针时,地址必须不同,因为AB 具有不同的vtable。 C++ 会自动进行正确的指针运算以获取有效的基类指针。

          如果你强制转换,无论是通过reinterpret_cast 还是先转换为void*,那么&amp;c 的原始类型就会丢失(这本质上是类型擦除)。

          A* aa = reinterpret_cast<A*>(&c);
          aa->f();   // UB ! (might work accidentally if A is the first baseclass of C)
          B* bb = reinterpret_cast<B*>(&c);
          bb->f();   // UB ! (probably will not work. In my test, it crashes the program.)
          

          此示例不起作用,您可能会遇到段错误或其他一些 UB。

          【讨论】:

            猜你喜欢
            • 2013-04-09
            • 2014-08-16
            • 2013-08-26
            • 2019-05-14
            • 1970-01-01
            • 2015-03-20
            • 1970-01-01
            • 2012-03-04
            • 2011-08-13
            相关资源
            最近更新 更多