C++ 在面向对象编程中,有着静态绑定和动态绑定的定义,为了了解这两个概念,首先先简单阐述一些名词:
- 静态类型:对象在声明时使用的类型,在编译期就已经确定
- 动态类型:指针变量或引用变量所指向对象的类型,在运行期才能确定
- 静态绑定:绑定的是静态类型,对象的函数和属性依赖于绑定的静态类型,发生在编译期
- 动态绑定:绑定的是动态类型,对象的函数和属性依赖于绑定的动态类型,发生在运行期
而非虚函数一般都是静态绑定,虚函数则是动态绑定。
举一个例子来说明静态类型和动态类型:
class Shape {//用于描述几何形状的类
public:
enum ShapeColor { Red, Green, Blue };
//所有形状都必须提供一个函数来绘制自己
virtual void draw(ShapeColor color = Red) const = 0;
void draw() const;
...
};
class Rectangle : public Shape {
public:
//注意,与基类的默认参数值不同
virtual void draw(ShapeColor color = Green) const;
...
};
class Circle : public Shape {
public:
//没有默认参数值,调用该函数一定要赋予参数,因为静态绑定下,这个函数并不从其基类继承默认参数值
//但若以指针或引用调用此函数,可以不指定参数值,因为动态绑定下,这个函数会从其基类继承默认参数值
virtual void draw(ShapeColor color) const;
...
};
int main(){
// 静态类型都为 Shape* 。注意,不论它们真正指向什么,它们的静态类型都是 Shape*
Shape* ps; // ps 没有指向,所以它没有动态类型
Shape* pNull = nullptr; // pNull 没有指向,所以它没有动态类型
Shape* pc = new Circle; // pc 的动态类型为 Circle*
Shape* pr = new Rectangle; // pr 的动态类型为 Rectangle*
ps = pc; // ps 的动态类型如今是 Circle*
ps = pr; // ps 的动态类型如今是 Rectangle*
return 0;
}
由于 虚函数 是动态绑定的,所以调用一个 虚函数 时,究竟调用哪个函数取决于发出调用的那个对象的 动态类型:
pc->draw(Shape::Red); // 调用 Circle::draw(Shape::Red)
pr->draw(Shape::Red); // 调用 Rectangle::draw(Shape::Red)
pNull->drwa(); // 调用 Shape::draw() ,空指针可以调用非虚函数,因为静态绑定在编译期就确定了,和指针空不空没关系
总结一下静态绑定和动态绑定的区别:
- 静态绑定发生在编译期,动态绑定发生在运行期
- 对象的动态类型可以更改,但是静态类型无法更改
- 要想实现动态,必须使用动态绑定
- 在继承体系中只有虚函数使用的是动态绑定,其他的全部是静态绑定;