为什么这个代码:
#include <iostream>
class Parent
{
public:
virtual Parent* getThis() { std::cout << "called Parent::getThis()\n"; return this; }
void printType() { std::cout << "returned a Parent\n"; }
};
class Child : public Parent
{
public:
virtual Child* getThis() { std::cout << "called Child::getThis()\n"; return this; }
void printType() { std::cout << "returned a Child\n"; }
};
int main()
{
Child ch;
Parent *p = &ch;
ch.getThis()->printType();
p->getThis()->printType();
}
它会产生以下结果吗?
called Child::getThis()
returned a Child
called Child::getThis()
returned a Parent
实际上,在最后一行应该显示returned a Child
它,因为它p->getThis()
返回一个指向Child
首先,值得了解的是,对象有静态类型,也有动态类型。静态类型在编译时设置,动态类型在程序执行期间定义。
例如,指向声明为
Parent* p;
具有静态类型的基类的指针Parent*
。动态类型可以与静态类型相同,也可以指向派生类型,例如Child*
. 调用特定指针的成员函数的选择基于函数的虚拟性。如果函数声明为虚拟,则使用对象的动态类型;如果函数不是虚拟的,则使用对象的静态类型。为了方便进一步阅读,我将在此处引用您的部分代码:
当被调用
ch.getThis()->printType();
时,第一部分,即ch.getThis()
有一个静态类型Child*
,因为 在编译时很明显正在使用派生类函数Child
。这意味着后续调用->printType()
将准确调用派生类的函数(尽管printType
它不是虚拟的)并显示字符串"returned a Child"
。当被调用
p->getThis()->printType();
时,第一部分,即p->getThis()
具有静态类型Parent*
,因为 函数返回的类型Parent::getThis()
是Parent*
. 在这种情况下,动态类型将是Child*
,因为p
指向派生类的对象,被调用的函数getThis()
是虚函数。但如前所述,对于非虚函数,动态类型不起作用,重要的是静态类型。因此,最终调用->printType()
从基类中准确选择函数并输出"returned a Parent"
。在您的情况下, getThis() 是一个虚函数,而 printType 不是,因此它不会重叠。
对于您期望的行为,您也需要将其设为虚拟。试试这样:
我的结论:
Parent*
基类指针指向它的继任者并不重要 。每种类型都有自己的成员函数,只有它的成员函数才能通过指向该类型的指针来调用。例外是虚函数,它提供运行时多态性,这是您所期望的行为。但是成员函数Parent::printType
不是虚拟的(指向隐式存储对象的虚拟函数的指针不能指向它)。如果您对getThis
返回的内容感到困惑,请Child*
小心:这条线
p->getThis()->printType();
是一样的因为 getThis 在任何情况下都应该返回
Parent*
,但是由于派生类中的多态性和覆盖,你得到了Child*
,但它仍然被视为Parent*
(当被覆盖时,返回类型不会改变)但是,对我来说,你认为应该发生这样的事情: