《Effective C++》:条款36-条款37
条款36:绝不重新定义继承而来的non-virtual函数以1个例子来展开本条款论述内容。假定class D是class B的派生类,class B中有1个public成员函数mf:
由1下方式调用
上面的两次调用函数
之所以行动不1致,是由于non-virtual函数是静态绑定的(statically bound,条款 37)。pB被声明为1个pointer-to-B,通过pB调用的non-virtual函数永久是B所定义的版本。但是virtual函数是动态绑定(dynamically bound,条款 37),所以virtual函数不受这个束缚,即通过指针调用,实际调用的函数是指针真正指向对象的那个函数。 如果你打算在class D中重新定义继承自class B的non-virtual函数,D对象极可能会出现行动不1致行动。更明确1点,即任何1个D对象都可能表现出B或D的行动;决定因素不在对象本身,而在于“指向该对象之指针”当初声明类型。References也会展现出和指针1样难以理解的行动。 前面已说过,public继承是is-a 关系(条款 32)。**条款**34说过,class内声明1个non-virtual函数会为该class建立1个不变性(invariant),它凌驾其特异性(specialization)。将这两个观点实施到class B和class D上和non-virtual函数B::mf上,那末
现在在D中重新定义 不论上面那个观点,结论都相同:任何情况下都不应当重新定义1个基础而来的non-virtual函数。 在条款 7已知道,base class内的析构函数应当是virtual;如果你违背了条款 7,你也就违背了本条款,由于析构函数每一个class都有,即便你没有自己编写。 总结
条款37:绝不要重新定义继承而来的缺省参数值在继承中,只能继承两种函数:virtual和non-virtual。在条款 36中我们学到,不能重新定义1个继承而来的non-virtual函数。本条款讨论的是继承virtual函数问题,再具体1点:继承1个带有缺省参数值的virtual函数。 我们应当知道,virtual函数是动态绑定(dynamically bound),缺省参数值却是静态绑定(statically bound)。 对象的静态类型(static type)是它在程序中被声明时采取的类型,例如
这个继承很简单。现在这样使用
这些指针类型都是pointer-to-Shape类型,都是静态类型Shape*。对象的动态类型是指“目前所指对象类型”。动态类型可以表现出1个对象将会有甚么行动。pc动态类型是Circle*,pr动态类型是Rectangle*,ps没有动态类型(它没有指向任何对象)。动态类型可以在履行进程中改变,重新赋值可以改变动态类型。 virtual函数是动态绑定的,调用哪1份函数实现的代码,取决于调用的那个对象的动态类型。
这样调用无可非议,都带有参数值。但是如果不带参数值呢
上面调用中,pr动态类型是Rectangle*,所以调用Rectangle的virtual函数。Rectangle::draw函数缺省值是GREEN,但是pr是静态类型Shape*,所以这个调用的缺省参数值来自Shape class,不是Rectangle class。这次调用两个函数各出了1半的力。 C++之所以使用这么奇异的运作方式,是由于效力问题。如果缺省参数值动态绑定,编译器必须有某种办法在运行期为virtual函数决定适当的参数缺省值。这比目前实行的“在编译器决定”的机制更慢且更复杂。为了履行速度和编译器实现上的简易度,C++做了这样的取舍。 我们尝试遵照这个规则,给base class和derived class提供相同参数值
这样问题又来了,代码重复且带着相依性(with dependencies):如果Shape内缺省参数值改变了,那末derived classes的缺省参数值也要改变,否则就会致使重复定义1个继承而来的缺省参数值。 当时如果的确需要derived classes的缺省参数值,那末就需要替换方法。条款 35列出了1些virtual函数的替换方法,例如NVI手法:
由于non-virtual函数不会被derived覆写(条款 36),这个设计很清楚的使得draw函数的color缺省参数值总是Red。 总结
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |