美文网首页C++
[C++] 动态绑定和静态绑定

[C++] 动态绑定和静态绑定

作者: 何幻 | 来源:发表于2017-05-04 16:08 被阅读78次

你不应该重新定义一个继承而来的non-virtual函数。
为继承而来的virtual函数,重新定义缺省参数值,也很糟糕。

因为,virtual函数系动态绑定(dynamically bound),
而缺省参数值却是静态绑定(statically bound)。

1. 静态类型

对象的所谓静态类型(static type),就是它在程序中被声明时所采用的类型,
考虑以下的class继承体系:

// 一个用以描述几何形状的class
class Shape{
public:
    enum ShapeColor { Red, Green, Blue };

    // 所有形状都必须提供一个函数,用来绘制出自己
    virtual void draw(ShapeColor color = Red) const = 0;

    ...
};
class Rectangle: public Shape{
public:
    // 注意,赋予不同的缺省参数值,这真糟糕
    virtual void draw(ShapeColor color = Green) const;

    ...
};

class Circle: public Shape{
public:
    virtual void draw(ShapeColor color) const;
};

现在考虑这些指针:

// 以下三个指针ps,pc,pr的静态类型,都是Shape*
Shape* ps;
Shape* pc = new Circle;
Shape* pr = new Rectangle;

2. 动态类型

对象的所谓动态类型(dynamic type)则指的是“目前所指对象的类型”,
也就是说,动态类型可以表现出一个对象将会有什么行为。
以上例而言,pc的动态类型是Circle*,pr的动态类型是Rectangle*,ps没有动态类型,因为它尚未指向任何对象。

动态类型一如其名称所示,可在程序执行过程中改变(通常是经由赋值动作):

// ps的动态类型如今是Circle*
ps = pc;

// ps的动态类型如今是Rectangle*
ps = pr;

virtual函数系动态绑定而来,意思是调用一个virtual函数时,究竟调用哪一份函数实现代码,
取决于发出调用的那个对象的动态类型。

// 调用Circle::draw(Shape::Red)
pc->draw(Shape::Red);

// 调用Rectangle::draw(Shape::Red)
pr->draw(Shape::Red);

3. 缺省参数值

但是当你考虑带有缺省参数值的virtual函数,花样来了,
virtual函数是动态绑定,而缺省参数值却是静态绑定。
意思是,你可能会在“调用一个定义于derived class内的virtual函数”的同时,却使用base class为它所指定的缺省参数值。

// 调用Rectangle::draw(Shape::Red)
pr->draw();

此例之中,pr的动态类型是Rectangle*,所以调用的是Rectangle的virtual函数,一如你所预期,
Rectangle::draw函数的缺省参数值应该是Green,但由于pr的静态类型是Shape*
所以,此一调用的缺省参数值来自Shape class而非Rectangle class。
结局是这个函数调用有着奇怪并且几乎绝对没人预料得到的组合,由Shape class和Rectangle class的draw声明式各出一半力。

以上事实不只局限于“ps,pc和pr都是指针”的情况,即使把指针换成reference问题仍然存在。
重点在于draw是个virtual函数,而它的缺省参数值在derived class中被重新定义了。

这一切还好,但如果你试着遵守这条规则,并且同时提供缺省参数值给base和derived class的用户,又会发生什么事呢?

class Rectangle: public Shape{
public:
    virtual void draw(ShapeColor color = Red) const;
    ...
};

喔欧,代码重复。更糟糕的是,代码重复又带有相依性(with dependencies)。
如果Shape内的缺省参数值改变了,所有的“重复给定缺省参数值”的那些derived class也必须改变,
否则它们最终会导致“重复定义一个继承而来的缺省参数值”。
怎么办?

4. non-virtual interface

当你想令virtual函数表现出你所想要的行为但却遭遇麻烦,聪明的做法是考虑替代设计。
其中之一是NVI(non-virtual interface)手法:
令base class内的一个public non-virtual函数调用private virtual函数,后者可被derived class重新定义。
这里我们可以让non-virtual函数指定缺省参数,而private virtual函数负责真正的工作。

class Shape{
public:
    enum ShapeColor { Red, Green, Blue };

    // 如今它是non-virtual
    void draw(ShapeColor color = Red) const {

        // 调用一个virtual
        doDraw(color);
    }
    ...

private:
    // 真正的工作在此处完成
    virtual void doDraw(ShapeColor color) const = 0;
};
class Rectangle: public Shape{
public:
    ...

public:
    // 注意,不须制定缺省参数值
    virtual void doDraw(ShapeColor color) const;
};

由于non-virtual函数绝对不应该被derived class覆写,这个设计很清楚的使用draw函数的color缺省值总是为Red。


Effective C++ - P180

相关文章

  • C++动态绑定和静态绑定

    动态绑定和静态绑定 静态绑定是编译期间就可以确定的类型 动态绑定则只能在运行时确定 用C++描述轿车和卡车 可以抽...

  • GEEKBAND C++ 面向对象高级编程(第四周)

    C++ 动态绑定和静态绑定 首先明确四个名词定义: 静态类型:对象在声明时的类型,编译期就能确定 动态类型:指针或...

  • 程序语言基础知识

    把一个方法与其所在的类/对象关联起来叫做方法的绑定,绑定分为静态绑定(前期绑定)和动态绑定(后期绑定)。 静态绑定...

  • 静态绑定和动态绑定

    绑定:指的是一个方法的调用与方法所在的类(方法主体)关联起来。(其实就是具体调用哪个方法)在java中,两种绑定方...

  • [C++] 动态绑定和静态绑定

    你不应该重新定义一个继承而来的non-virtual函数。为继承而来的virtual函数,重新定义缺省参数值,也很...

  • 静态绑定(前期绑定) 与 动态绑定(后期绑定)

    绑定   绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来,绑定分为静态绑定(前期绑定) 和 动态绑定(...

  • Java静态绑定与动态绑定

    目录介绍 01.什么是绑定 02.静态和动态绑定 03.辅助理解jvm图 04.动态绑定编译原理 05.动态绑定运...

  • angular 模板里面绑定属性

    1. 绑定静态属性 static property example 2. 绑定动态属性 动态属性绑定要用 [] /...

  • Java-动态绑定和静态绑定

    绑定 一个方法的调用与方法所在的类关联起来。java中的绑定分为静态绑定和动态绑定,又被称作前期绑定和后期绑定。 ...

  • 第十一条 消息传递机制 objc_msgSend

    静态绑定/动态绑定# 静态绑定,就是在编译期就能决定运行时所调用的函数。动态绑定,就是在运行期才能确定调用函数。 ...

网友评论

    本文标题:[C++] 动态绑定和静态绑定

    本文链接:https://www.haomeiwen.com/subject/bgqdtxtx.html