首先注意四个概念:对象的静态类型、对象的动态类型、静态绑定、动态绑定;
1、对象的静态类型(static type):就是它在程序中被声明时所采用的类型(或理解为类型指针或引用的字面类型),在编译期确定;
2、对象的动态类型(dynamic type):是指“目前所指对象的类型”(或理解为类型指针或引用的实际类型),在运行期确定;
这两个概念一般发生在基类和派生类之间。比如:
class Shape
{
};
class Rectangle : public Shape
{
};
class Circle : public Shape
{
};
Shape *ps = nullptr; // ps的静态类型为Shape*,它没有动态类型,因为它尚未指向任何对象;
Shape *pc = new Circle; // pc的静态类型为Shape*,它的动态类型为Circle*;
Shape *pr = new Rectangle; // pr的静态类型为Shape*,它的动态类型为Rectangle*;
// 动态类型可以在程序执行过程中改变(通常是经由赋值动作):
ps = pc; // ps的动态类型如今是Circle*;
ps = pr; // ps的动态类型如今是Rectangle*;
3、静态绑定(statically bound):又名前期绑定(eraly binding),绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在 编译期;
4、动态绑定(dynamically bound):又名后期绑定(late binding),绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;
比如常见的,virtual函数是动态绑定,non-virtual函数是静态绑定,缺省参数值也是静态绑定。
以virtual函数为例。当某个virtual函数通过指针或引用调用时,编译器产生的代码直到运行时才能确定应该调用哪个版本的函数。被调用的函数是与绑定到指针或引用上的对象的动态类型相匹配的那一个。需要注意的是:动态绑定只有当我们通过指针或引用调用虚函数时才会发生。(如果通过一个具有普通类型(非指针非引用)的表达式调用虚函数时,在编译时就会将调用的版本确定下来。例如下面例子中的shape)
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void draw()
{
cout << "Shape::draw()" << endl;
}
};
class Circle : public Shape
{
public:
virtual void draw()
{
cout << "Circle::draw()" << endl;
}
};
int main()
{
Shape *ps = new Shape;
Circle *pc = new Circle;
ps->draw(); // Shape::draw() ; 因为ps的动态类型如今是Shape*;
pc->draw(); // Circle::draw() ; 因为pc的动态类型如今是Circle*;
ps = pc;
ps->draw(); // Circle::draw() ; 因为ps的动态类型如今是Circle*;
pc->draw(); // Circle::draw() ; 因为pc的动态类型如今是Circle*;
Shape shape;
Circle circle;
shape.draw(); // Shape::draw() ;
circle.draw(); // Circle::draw() ;
shape = circle;
shape.draw(); // Shape::draw() ; 因为当通过一个具有普通类型(非指针非引用)的表达式调用虚函数时,在编译时就已将调用的版本确定下来。
circle.draw(); // Circle::draw();
system("pause");
return 0;
}
non-virtual函数是静态绑定:因此“绝不重新定义继承而来的non-virtual函数”,因为这样会导致函数的调用类型由对象声明时的静态类型确定,而与对象本身脱离了关系。详见《Effective C++》条款36.
#include <iostream>
using namespace std;
class Shape
{
public:
void draw()
{
cout << "Shape::draw()" << endl;
}
};
class Circle : public Shape
{
public:
void draw()
{
cout << "Circle::draw()" << endl;
}
};
int main()
{
Circle circle; // 定义一个Circle对象
Shape *ps = &circle; // 获得一个指针指向circle
ps->draw(); // 经由该指针调用mf,输出 Shape::draw() ; 一个Circle对象调用draw,产生了Shape行为;
Circle *pc = &circle; // 获得一个指针指向circle
pc->draw(); // 经由该指针调用mf,输出 Circle::draw() ; 一个Circle对象调用draw,产生了Circle行为;
// 总结:ps和pc都指向一个Circle对象,但调用draw()时,却表现出了Shape或Circle的行为。
// 这种莫名其妙的错误(或不一致行径)的原因在于:其决定因素不在对象自身,而在于“指向该对象之指针”当初的声明类型。
system("pause");
return 0;
}