首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C++:47---绝不重新定义继承而来的缺省参数值

C++:47---绝不重新定义继承而来的缺省参数值

作者头像
用户3479834
发布2021-02-03 14:51:20
发布2021-02-03 14:51:20
5660
举报
文章被收录于专栏:游戏开发司机游戏开发司机

一、静态类型、动态类型

  • 静态类型:在被声明时所采用的的类型
  • 动态类型:目前所知对象的类型

演示案例

  • 下面是一个继承体系
代码语言:javascript
复制
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 = 0;
};


class Circle :public Shape {
public:
virtual void draw(ShapeColor color)const = 0;
};
  • 现在我们定义下面的代码,它们都被声明为pinter-to-Shpae类型,因此它们不论它们指向什么,静态类型都是Shape*:
代码语言:javascript
复制
Shape* ps;                 //静态类型为Shape*
Shape* pc = new Circle;    //静态类型为Shape*
Shape* pr = new Rectangle; //静态类型为Shape*
  • 动态类型是指该该对象将会有什么行为。例如:
代码语言:javascript
复制
Shape* ps;
Shape* pc = new Circle;
Shape* pr = new Rectangle;


ps = pc; //ps的动态类型如今是Circle*
ps = pr; //ps的动态类型如今是Rectangle*
  • 根据语法我们知道,对于virtual函数的调用,是根据其动态类型决定的。例如:
代码语言:javascript
复制
Shape* ps;
Shape* pc = new Circle;
Shape* pr = new Rectangle;


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

二、virtual函数的缺省参数值是静态绑定的

  • 虽然对于virtual函数的调用时动态绑定的,但是对于virtual函数的缺省参数值却是静态绑定的
  • 见下面的代码:
    • 我们知道virtual函数是动态绑定的,pr的动态类型为Rectangle,所以调用的是Rectangle::draw()
    • 但是virtual函数的缺省参数值是静态绑定的,在上面类的定义中Rectangle的draw()函数无参数,但是由于pr指针的静态类型是Shape,因此其draw()函数的缺省参数值就是Shape::draw()函数中的参数值,为Shape::Red
代码语言:javascript
复制
Shape* pr = new Rectangle;
pr->draw(); //调用的是Rectangle::draw(Shape::Red)
//Circle也是相同的道理
Shape* pc = new Circle;
pc->draw(); //调用的是Circle::draw(Shape::Red),
//而不是Circle::draw(Shape::Green)
  • 为什么要设计这种行为:在于运行效率。如果缺省参数值也是动态绑定,编译器就必须有某种办法在运行期为virtual函数决定适当的参数缺省值,这比目前实行的“在编译期决定”的机制更慢而且更复杂

三、不要重新定义继承而来的缺省参数值

  • 通过二,我们知道virtual函数的缺省参数值是静态绑定的。因此,我们不要重新定义继承而来的缺省参数值,因为这会在调用virtual函数时产生意想不到的效果(上面代码中,通过pc调用draw()就是一个例子)

四、针对于virtual函数的缺省参数值,给出的建议

先看一个效率低下的方案

  • 为了保持基类与派生类中的一致性,一种低效率的方法是将基类和派生类中的virtual函数的缺省参数值设置为一致的
  • 例如:
代码语言:javascript
复制
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 = Red)const;
};


class Circle :public Shape {
public:
virtual void draw(ShapeColor color = Red)const;
};
  • 低效率的原因:
    • ①代码重复
    • ②依赖性太高,如果基类中的缺省参数值改变了,那么需要将派生类中的缺省参数值都修改一遍

以NVI手法定义class

  • 对于virutal函数的缺省参数值,为了避免基类与派生类中的缺省参数值不一致,我们可以采取这种方法
  • 定义的代码如下:
代码语言:javascript
复制
class Shape {
public:
enum ShapeColor { Red, Green, Blue };
void draw(ShapeColor color = Red)const { //因为是non-virtual函数,因此不建议派生类隐藏
doDraw(Red);
}
private:
//真正的工作在此处完成,派生类可以重写
virtual void doDraw(ShapeColor color)const = 0;
};




class Rectangle :public Shape {
private:
virtual void doDraw(ShapeColor color)const = 0;
};
  • 上面的doDraw()函数完成真正的功能,并且接受一个ShapeColor类型
  • 我们又定义了一个non-virtual函数draw,其参数默认为Red,并且non-virtual函数不建议派生类隐藏,因此不论是基类还是派生类调用draw()函数,参数的默认值将永远是Red,达到了我们最终的目的

五、总结

  • 绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virutal函数——你唯一应该覆盖的东西——却是动态绑定
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、静态类型、动态类型
    • 演示案例
  • 二、virtual函数的缺省参数值是静态绑定的
  • 三、不要重新定义继承而来的缺省参数值
  • 四、针对于virtual函数的缺省参数值,给出的建议
    • 先看一个效率低下的方案
    • 以NVI手法定义class
  • 五、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档