子类中含有父类的接口,所以在任何需要基类指针或引用的地方,我们都可以为其提供一个派生类的指针或引用。在任何需要基类副本的地方,我们也可以为其提供一个派生类。但是指针和引用的地方是动态类型,而副本的地方是静态类型。
表达式的静态类型在编译时总是已知的,而动态类型则是变量或表达式表示的内存中的对象的类型(一个变量表示的内存中的对象类型可能不唯一)。动态类型直到运行时才可知。
如 Father item1;//基类对象
Son item2;//派生类对象
Father* ptr=&item1;//ptr指向基类对象
ptr=&item2;//ptr指向派生类中的基类部分
Father & a=Son;//a绑定到派生类中的基类部分
2.派生类中被声明为虚函数的函数会重写基类中被声明为虚函数的同名函数。
考虑如下代码:
#include<iostream>
using namespace std;
class father {
public:
virtual int hehe(void) {
return 1;
}
};
class son :public father
{
public:
int hehe(void)
{
return 2;
}
};
int hey(father&);
int main()
{
son c;
cout << hey(c) << endl;
father& a = c;
cout << hey(a);
system("pause");
return 0;
}
int hey(father& a)
{
return a.hehe();
}
输出为2,2。因为重名函数被声明为虚函数,所以基类中的被抹去重写。如果没有被声明为虚函数,输出为1,1 。而且如果函数形参为father而不是引用,输出2,1 。
原理是不是很乱?我们可以这样理解:
1.所有需要基类指针和引用的地方我们都可以用派生类的指针和引用来代替,此时为动态类型,也就相当于传入了一个派生类,那么自然调用派生类中的虚函数。如果函数重名但并未被声明虚函数,那么调用基类中的重名函数。值得注意的是,如果此时直接调用,调用的是派生类中的重名函数。
2.所有需要基类副本的地方都可以用派生类来代替,此时为静态类型,调用的永远都是基类。
3.存在派生类指针或引用向基类的指针或引用的转换,但不存在基类的指针或引用向派生类指针或引用的转换。一个派生类的指针不能指向一个基类,而一个基类的指针可以指向一个派生类,此时该基类指针的动态类型为派生类指针。