C++ 动态绑定和静态绑定
首先明确四个名词定义:
-
静态类型:对象在声明时的类型,编译期就能确定
-
动态类型:指针或引用所指的对象的类型
-
静态绑定:绑定的是静态类型,依赖于对象的静态类型
-
动态绑定:绑定的是动态类型,依赖于对象的动态类型
类的非虚函数都是静态绑定,虚函数都是动态绑定。
下面代码中,
#include <iostream>
using namespace std;
class Fruit
{
public:
void print(){cout << "Fruit::print()" << endl;}
virtual void process(){cout << "Fruit::process()" << endl;}
};
class Apple : public Fruit
{
public:
void print(){cout << "Apple::print()" << endl;}
virtual void process(){cout << "Apple::process()" << endl;}
};
class Pear : public Fruit
{
public:
void print(){cout << "Pear::print()" << endl;}
virtual void process(){cout << "Pear::process()" << endl;}
};
int main() {
Pear* pc = new Pear();
Apple* pb = new Apple();
Fruit* pa = new Fruit();
pa = pb;
pa->print();
pb->print();
pc->print();
pa->process();
pb->process();
pc->process();
return 0;
}
- pa的静态类型是Fruit,动态类型先是pc指向的Pear,后被改成pb指向的Apple
- pb的静态类型和动态类型都是Apple
- pb的静态类型和动态类型都是Pear
代码执行的结果为:
Fruit::print()
Apple::print()
Pear::print()
Apple::process()
Apple::process()
Pear::process()
可以看到对于类中非虚函数的调用,是静态类型决定的,在编译器就能确定;而虚函数的调用,是动态类型决定的,在运行期才能确定。在运行期确定函数的调用,就是面向对象中所说的多态。
注意
《Effective C++》中建议
1.绝对不要重新定义继承而来的非虚(non-virtual)函数
2.绝对不要重新定义一个继承而来的virtual函数的缺省参数值,因为缺省参数值都是静态绑定(为了执行效率),而virtual函数却是动态绑定。
#include <iostream>
using namespace std;
class Fruit
{
public:
void print(){cout << "Fruit::print()" << endl;}
virtual void process(int i = 1){cout << "Fruit::process(), i="<< i << endl;}
};
class Apple : public Fruit
{
public:
void print(){cout << "Apple::print()" << endl;}
virtual void process(int i = 10){cout << "Apple::process(), i="<< i << endl;}
};
class Pear : public Fruit
{
public:
void print(){cout << "Pear::print()" << endl;}
virtual void process(int i = 20){cout << "Pear::process(), i="<< i << endl;}
};
int main() {
Pear* pc = new Pear();
Apple* pb = new Apple();
Fruit* pa = pc;
pa = pb;
pa->print();
pb->print();
pc->print();
pa->process();
pb->process();
pc->process();
return 0;
}
Fruit::print()
Apple::print()
Pear::print()
Apple::process(), i=1
Apple::process(), i=10
Pear::process(), i=20
上面的代码,pa的process函数调用的是子类的process,但是参数的默认值却是父类的默认值。