【问题标题】:Does C++ allow multiple levels of virtualness?C++ 是否允许多级虚拟性?
【发布时间】:2011-11-01 09:08:57
【问题描述】:

我有一个名为 Object 的基类。 PhysicsObject 继承自 Object。 Ball 继承自 PhysicsObject,SoftBall 继承自 Ball。像这样的:

Object
 |
PhysicsObject
 |
Ball
 |
SoftBall

我有一个名为 foo() 的方法,它在 Object 中声明为虚拟(给定一个实现,因此不是纯虚拟),然后在 PhysicsObject 和 Ball 中再次声明并实现为虚拟。最后,SoftBall 再次实现了 foo() 而没有将其声明为虚拟。

如果我有一个指向 SoftBall 的 Object*,是否会调用 SoftBall 的 foo() 版本?如果没有,有什么方法可以达到这个效果?基本上,多态性的这一方面是否仍然适用于多个继承级别?

【问题讨论】:

    标签: c++ oop inheritance polymorphism virtual


    【解决方案1】:

    如果我有一个指向 SoftBall 的 Object*,是否会调用 SoftBall 的 foo() 版本?如果没有,有什么方法可以达到这个效果?基本上,多态性的这一方面是否仍然适用于多个继承级别?

    是的

    如果相应的重载在基类中是虚拟的,则 C++ 将方法定义为虚拟(与其他 OO 语言不同,必须显式标记覆盖)。

    http://codepad.org/pL09QWNN


    请注意,如果添加更多成分,您会变得非常时髦:

    #include <iostream>
    
    struct A { virtual void foo() { std::cout << "A::foo();" << std::endl; } };
    struct B { virtual void foo() { std::cout << "B::foo();" << std::endl; } };
    
    struct Oa: A, B { using A::foo; };
    struct Ob: A, B { using B::foo; };
    
    #define TEST(a) std::cout << #a << ":\t"; a
    
    int main()
    {
        A a;
        B b;
        Oa oa;
        Ob ob;
    
        TEST(a.foo());
        TEST(b.foo());
        TEST(oa.foo());
        TEST(ob.foo());
    
        std::cout << "But oa through different references:" << std::endl;
        {
            A& ar = oa;
            TEST(ar.foo());
    
            B& br = oa;
            TEST(br.foo());
        }
    
        std::cout << "And ob through different references:" << std::endl;
        {
            A& ar = ob;
            TEST(ar.foo());
    
            B& br = ob;
            TEST(br.foo());
        }
    
        return 0;
    }
    

    尝试预测此处打印的内容。混入后会更有趣:

    • (可变/const/volatile 限定)重载
    • (冲突的)默认参数

    我记得我读过很多以琐事问题形式出现的可怕的例子,我希望在面试中永远不会遇到。虽然我知道我会回答:“如果你有这样的代码,我不确定我是否想要这份工作”?

    你可以去看看 Herb Sutter、Scott Meyer,你会惊讶于我们原本如此文明的小语言 C++ 中潜伏着什么陷阱

    【讨论】:

    • “基类”基本上是“任何基类”(这确实是问题的重点)
    • 添加了更多背景,但主要是离题
    • 啊哈哈哈……“文明”? “好的”? “小的”? “被人津津乐道”?!你没有跑题;你疯了;)
    【解决方案2】:

    如果你在基类中声明一个虚函数,并在派生类中声明一个具有相同签名的函数,它会自动变为虚函数(即使在之间)。所以你的问题的答案是,是的,foo() 的 SoftBall 实现将被调用。

    但是请注意,如果签名有任何差异,那么虚函数将被隐藏。例如:

    class Object {
    public:
      virtual void foo();
    };
    
    class InBetween : public Object {
      // assume it has no foo()s
    };
    
    class A : public InBetween {
    public:
      void foo() const; // DOES NOT OVERRIDE; ((Object *)pA)->foo() calls Object::foo()
                        // However pA->foo() calls this
    };
    
    class B: public InBetween {
    public:
      void foo() const; // Does not override; pB->foo() calls this if pB is const
      void foo(); // Does override; ((Object *)pB)->foo() calls this. pB->foo() also calls this if pB is non-const
    };
    

    【讨论】:

      【解决方案3】:

      如果我有一个指向 SoftBall 的 Object*,SoftBall 的 调用 foo() 的版本?如果没有,有什么办法可以做到这一点 影响?基本上,多态性的这个方面是否仍然有效 不止一层继承?

      是的。

      当然,you could simply have tried it... ;)


      为了后代,这是我的测试用例(假设cout 在范围内):

      struct Object {
         virtual void foo() {}
      };
      
      struct PhysicsObject : Object {};
      struct Ball : PhysicsObject {};
      
      struct SoftBall : Ball {
         virtual void foo() {
            cout << "SoftBall";
         }
      };
      
      int main() {
         SoftBall sb;
         Object* o = &sb;
         o->foo();
      }
      
      // Output: "SoftBall"
      

      【讨论】:

      • 绝对正确。我发布了这个问题,因为我有一个像这样设置的项目(一个 Breakout 克隆),但它的功能就好像多态性的这个方面不起作用。所以现在我知道问题出在我写的东西上,而不是语言的限制:D
      • @Lewis:*点头* 明白。见证将您的问题简化为一个简单的测试用例以缩小问题范围的力量 :)
      • 顺便说一句,在这种情况下,我怀疑您在某些时候无意中对对象进行了切片,或者(我认为不太可能)由于签名不匹配,您的覆盖实际上并未被覆盖。
      猜你喜欢
      • 1970-01-01
      • 2016-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-04
      • 2013-04-29
      相关资源
      最近更新 更多