只有通过基类指针或引用间接指向派生类子类型时,多态性才会起作用。使用基类对象并不会保留派生类的类型身份,不再具备多态性。
深入讨论:
这个问题和答案有一点模糊,让我们通过一个示例来明确这个问题。
#include <iostream> class Base { public: virtual void f() { std::cout<< “Calling Base::f()…\n”; }; };
class Derived: public Base { public: virtual void f() { std::cout<< “Calling Derived::f()…\n”; }; }; |
类Base和Derived,Derived从Base继承而来,子类Derived覆盖(Override)了基类Base中定义的虚函数f()。如果通过基类对象指针或引用来调用虚函数f(),那么会调用类对象的动态类型中所定义的函数,多态性发挥了作用。所以,如果执行如下代码:
Base* pb = new Base(); Base* pd = new Derived();
pb->f(); pd->f();
Base& rb = *pb; Base& rd = *pd;
rb.f(); rd.f(); |
会得到如下结果。
Calling Base::f()... Calling Derived::f()... Calling Base::f()... Calling Derived::f()... |
如果通过基类对象来调用虚函数f(),基类对象并不会保留派生类的类型身份,不再具备多态性。所以,如果执行如下代码:
Derived d; Base b = d; // d被“切割”成一个Base对象 d.f(); |
会得到如下结果。
Calling Base::f()... |
尽管用d初始化b是合法的,但是b不再是一个Derived对象。在初始化b之前,d的Derived部分被切除,成为了一个Base对象。所以,最后调用了方法Base::f()。这也是C++被人广为诟病的特性之一,作为一种面向对象编程语言,我们必须使用指针以引用而不是对象来支持面向对象程序设计。
可能有一些善于思考的读者会想到一种情况:如果基类对象通过解引用操作符*获得,调用虚函数是否具备多态性?例如,执行如下代码会得到什么结果:
Base* pb = new Base(); Base* pd = new Derived(); Base b = *pd; b.f(); (*pd).f(); |
执行结果如下:
Calling Base::f()... Calling Derived::f()... |
为什么(*pd).f()会调用Derived::f()?因为通过解引用操作符*获得不是一个基类Base对象,而是子类Derived对象。
参考资料:
《C++ Primer》第四版章节17.5导言部分。
《Inside C++ Object Model》章节1.3。
文章评论(0条评论)
登录后参与讨论