1、对象的静态类型和动态类型
- 对象的静态类型:对象在声明时采用的类型。是在编译期确定的。
- 对象的动态类型:目前所指对象的类型,是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。
关于对象的静态类型和动态类型,看一个示例:
classB
{
};
classC:publicB
{
};
classD:publicB
{
};
D*pD=newD(); // pD的静态类型是它声明的类型D*,动态类型也是D*
B*pB=pD; // pB的静态类型是它声明的类型B*,动态类型是pB所指向的对象pD的类型D*
C*pC=newC();
pB=pC; // pB的动态类型是可以更改的,现在它的动态类型是C*
2、静态绑定和动态绑定
- 静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。
- 动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期。
classB
{
voidDoSomething();
virtualvoidvfun();
};
classC:publicB
{
voidDoSomething(); // 首先说明一下,这个子类重新定义了父类的no-virtual函数,这是一个不好的设计,会导致名称遮掩;这里只是为了说明动态绑定和静态绑定才这样使用。
virtualvoidvfun();
};
classD:publicB
{
voidDoSomething();
virtualvoidvfun();
};
D*pD=newD();
B*pB=pD;
- pD->DoSomething()和pB->DoSomething()调用的不是同一个函数,虽然pD和pB都指向同一个对象。因为函数DoSomething是一个no-virtual函数,它是静态绑定的,也就是编译器会在编译期根据对象的静态类型来选择函数。pD的静态类型是D*,那么编译器在处理pD->DoSomething()的时候会将它指向D::DoSomething()。同理,pB的静态类型是B*,那pB->DoSomething()调用的就是B::DoSomething()。
- pD->vfun()和pB->vfun()调用的是同一个函数,因为vfun是一个虚函数,它动态绑定的,也就是说它绑定的是对象的动态类型,pB和pD虽然静态类型不同,但是他们同时指向一个对象,他们的动态类型是相同的,都是D*,所以,他们的调用的是同一个函数:D::vfun()。
- 上面都是针对对象指针的情况,对于引用(reference)的情况同样适用。指针和引用的动态类型和静态类型可能会不一致,但是对象的动态类型和静态类型是一致的。
D D;
D.DoSomething()和D.vfun()永远调用的都是D::DoSomething()和D::vfun()。
- 至于哪些是动态绑定,哪些是静态绑定,总结为一句话:只有虚函数才使用的是动态绑定,其他的全部是静态绑定。
3、一个困惑
看完上述讲解你可能有一个疑惑,即:“我看程序代码时,可以看出实际将会调用的函数,为何编译器不能在编译时确定将要调用的函数”
一个示例来解决你的困惑:
Object* obj;
std::cin >> type;
switch (type)
{
case PEOPLE: obj = new People;
break;
case MUSLIM: obj = new Sheep;
break;
}
obj->WhichFunc();
此时你还能看出将要调用哪个函数么?哈哈哈