将派生类地址赋给基类指针,也就是基类指针指派生类对象,也就是我们平时说的多态
但是反过来的时候,必须经过强制类型转换才可以编译通过,
下来直接代码:
#include<iostream>
usingnamespacestd;
class Base
{
public:
virtual void print()
{
cout<<"base"<<endl;
}
};
class Derived:public Base
{
public:
void print()
{
cout<<"derived"<<endl;
}
};
int main(void)
{
Base * pb=newB ase();//定义基类指针
Derived * pd=(Derived*)pb;//赋值给派生类指针
pd->print();//调用基类的print输出base
pd->Derived::print();//调用派生类的print输出derived
return 0;
}
我想问问用pd->print()为什么调用的是基类的print
然后为什么将基类的virtual去掉之后,调用的便是派生类的print
调用哪个虚函数是由对象所指的虚函数表所决定的,当你 new Base()的时候pb所指向的虚函数表中的虚函数是Base的,用(Derived*)强制转化并没改变,而非虚函数主要根据指针类型,也就是说pd一开始就是Derived,所以用Derived的函数,成员函数在调用时其实是调用了this指针的。
使用virtual的意思就是这个函数可以被子类override,使指向子类的父类指针不跟据指针本身的类别来调用这个函数,而(通过查虚函数表V-Table)调用被子类override后的函数。
值得一提的是
这样写是不正确,至少也是不安全的,不应该将指向父类对象的父类指针赋给子类指针。
此处的pd->print指向的是虚函数表中父类的的print,所以
调用的是Base::print
接下来
这是强行在父类对象上调用子类的函数(或者说以父类对象为this,强行调用虚函数表中的子类print),一般不推荐这么做。
还真是这样,刚运行了一下,确实很难理解。
我说一下我的思路,不知道对不对,大家可以参考一下:
第一种情况,写了virtual,如果成员函数加virtual,说明是动态绑定,只有在执行时候才会找到被调函数的地址。但是在哪里找呢?貌似每个对象的内存中都会存在一份虚表(也有说法是每个类有一份虚表,对象共享),因为你的Derived指针指向的是Base对象的那块内存,所以系统就去那块内存的虚表中找print函数,那块内存中虚表中的print函数的地址是Base类的print函数的地址,所以调用了基类的print函数
第二种情况:把virtual给去了,那就不涉及动态绑定,函数的地址是编译时候确定的。编译时候函数地址怎么确定呢:是通过在源程序符号表中查找所得。那么主程序中pd->print(),很明显,pd是Derived类,pd->print()相当于Derived::print,那编译器就到Derived类中找喽,所以编译时期把pd->print()中print()的地址替换成Derived::print的地址,所以就调用了子类的print函数。
不知道你明白我说啥不