《Effective C++》37: 绝不重新定义继承而来的缺省的参数值
动态绑定(dynamically bound)又名前期绑定(early binding),静态绑定(statically bound)又名后期绑定(late binding)。
所谓静态绑定是指在程序编译过程中,把函数(方法或者过程)调用与响应调用所需的代码结合的过程称之为静态绑定。
动态绑定是指在执行期间(非编译期)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法 除了限制访问,
访问方式也决定哪个方法将被子类调用或哪个属性将被子类访问. 函数调用与函数本身的关联,
以及成员访问与变量内存地址间的关系,称为绑定.动态绑定则针对运行期产生的访问请求,只用到运行期的可用信息.
在面向对象的代码中,动态绑定意味着决定哪个方法被调用或哪个属性被访问,将基于这个类本身而不基于访问范围.
—— 摘自网络
对象的静态类型(static type)就是它在程序中被声明时所采用的类型。
对象的动态类型(dynamic type)就是指目前所指向的对象的类型。
例如,
class Base
{
......
};
class Derive : public Base
{
......
};
Base * p;
p 的静态类型就是Base* ,因为它被Base*声明。
p没有动态类型,因为它没有指向任何的对象。
Base pB = new Base;
Base pD = new Derive;
pD和pB的静态类型都是Base*
pD的动态类型是Derive*,而pB的动态类型是Base*,因为他们实际指向的类型分别是Derive对象和Base对象。
virtual函数是动态绑定而来的,意思是调用一个virtual函数时,究竟调用哪一份函数实现代码,
取决于发出调用的那个对象的动态类型(而不是它的静态类型)。
但是,缺省参数(default parameter value)却是静态绑定的,应用哪一种缺省参数只取决于它的静态类型。
例如:
#include <iostream>
#include <string>
using namespace std;
class Widget
{
public:
virtual void SaySomething(string msg
= "I'm Widget's default parameter value.")
{
cout << "I'm in Widget." << endl;
cout << msg << endl;
}
};
class Label : public Widget
{
public:
virtual void SaySomething(string msg
= "I'm Label's default parameter value.")
{
cout << "I'm in Label." << endl;
cout << msg << endl;
}
};
int main()
{
Label* label = new Label;
//静态类型和动态类型都是Label*
//缺省参数和虚函数都调用Label类的
label->SaySomething();
cout << endl;
Widget* wl = new Label;
//静态类型是Widget*,动态类型是Label*
//缺省参数调用Widget类的,虚函数调用Label类的
wl->SaySomething();
cout << endl;
Widget* widget = new Widget;
//静态类型和动态类型都是Widget*
//缺省参数和虚函数都调用Widget类的
widget->SaySomething();
return 0;
}
实现效果:
这就意味着“调用一个定义于派生类内的虚函数”的同时,却又有可能使用基类为它所指定的缺省参数值。
因此建议:绝不重新定义继承而来的缺省参数值。
但如果你试着遵循这条规则,并提供同样的缺省参数值给基类和子类,又会怎么样呢?
class Widget
{
public:
virtual void SaySomething(string msg
= "I'm Widget's default parameter value.")
{
cout << "I'm in Widget." << endl;
cout << msg << endl;
}
};
class Label : public Widget
{
public:
virtual void SaySomething(string msg
= "I'm Widget's default parameter value.")
{
cout << "I'm in Label." << endl;
cout << msg << endl;
}
};
代码重复,带有相依性。如果基类改变了缺省参数,则要为所有的子类修改缺省参数值。《Effective C++》给出的修改方法是NVI手法。