设为首页 加入收藏

TOP

C++继承与面向对象设计(七)
2014-07-19 23:03:06 来源: 作者: 【 】 浏览:238
Tags:继承 面向 对象 设计

 

  4 考虑virtual函数之外的选择

  考虑为游戏内的人物设计一个继承体系。

  class GameCharacter {

  public:

  virtual int healthValue() const; // 返回人物的健康指数。

  ......

  };

  有时候,常规的面向对象设计方法往往看起来是那么的自然,以至于我们从未考虑其他的一些解法。

  这一节就让我们跳出常规设计的思维,考虑一些不那么常规的设计方法。

  方法1:借由non-virtual interface手法实现Template Method模式

  class GameCharacter {

  public:

  int healthValue() const {

  ...

  int retVal = doHealthValue();

  ...

  return retVal;

  }

  ....

  private:

  virtual int doHealthValue() const {

  ...

  }

  };

  让客户通过public non-virtual成员函数间接调用private virtual函数,称为non-virtual interface(NVI)手法。

  这个non-virtual函数(healthValue)称为virtual函数的包装器(wrapper)。

  从程序执行的角度来看,derived classes重新定义了virtual函数,从而赋予它们“如何实现功能”的控制能力,base classes保留控制“函数何时被调用”的权利。

  方法2:借由Function Pointer实现Strategy模式

  代码如下:

  class GameCharacter:;

  int defaultHealthCalc(const GameCharacter& gc);

  class GameCharacter {

  public:

  typedef int ( *HealthCalcFunc ) ( const GameCharacter& );

  explicit GameCharacter( HealthCalcFunc hcf = defaultHealthCalc ) : healthFunc(hcf) { }

  int healthValue() const {

  return healthFunc(*this);

  }

  ...

  private:

  HealthCalcFunc healthFunc;

  };

  还有其他的一些方法,在此并不一一讨论,详见《Effective C++

  5 绝不重新定义继承而来的non-virtual函数

  在子类中重定义继承而来的non-virtual函数,会导致子类你的设计出现矛盾。

  比如在class Base有一个non-virtual函数setBigger,而所有继承Base的子类都可以执行变大的动作,那么这个动作就是一个不变性(共性)。

  而在class Derived : public Base子类中,重写了setBigger函数,那么class Derived便无法反映出“不变性凌驾于特性”的性质。

  从另一方面说,如果setBigger操作真的需要在子类中重定义,那么就不应该把它设定为一个共性(non-virtual)。

  因此,重新定义继承来的non-virtual函数可能并不会对你的程序的运行造成太大的困扰,但是正如上面提到的,这是设计上的矛盾,或者说缺陷。

  6 绝不重新定义继承而来的缺省参数值

  本小节的讨论局限于“继承一个带有缺省参数值的virtual函数”。

  理由:virtual函数动态绑定,缺省参数值静态绑定。

  class Shape {

  public:

  Shape() {};

  enum ShapeColor { Red = "red", Green = "green" , Blue = "blue"};

  virtual void draw(ShapeColor color = Red) const = 0

  {

  std::cout << "This shape is " << color << std::endl;

  }

  };

  class Rectangle : public Shape {

  public:

  Rectangle() {};

  virtual void draw ( ShapeColor color = Green ) const;

  };

  class Circle : public Shape {

  public:

  virtual void draw(ShapeColor color) const;

  };

  先考虑如下指针

  Shape* ps;

  Shape* pc = new Circle;

  Shape* pr = new Rectangle;

  ps、pc、pr的静态类型都是Shape*

  所谓动态类型就是“目前所指对象的类型”。也就是说动态类型可以表现出一个对象将会有什么行为。

  在本例中,ps没有动态类型,pc的动态类型为Circle*,pr的动态类型为Rectangle*。

  动态类型可以在程序执行过程中改变(通常是经由赋值动作)。如

  ps = pc; // ps的动态类型现在是Circle

  ps = pr; // ps的动态类型现在是Rectangle

  上面是对动态绑定和静态绑定的简单复习。

  现在,考虑带有缺省参数值的virtual函数。

  在上面的例子中,Shape中的draw函数的color默认参数是Red,而子类中的draw函数的color默认参数是Green。

  Shape* shape = new Rectangle();

  shape->draw();

  根据动态绑定规则,上述代码的输出应该为:This shape is 1

  但是运行代码之后会发现,结果并不是我们想的那样。

clipboard

  我们来分析一下导致这种结果的原因:

  shape的动态类型为Rectangle*,调用draw时,根据动态绑定,调用的应该为Rectangle的版本

  Rectangle版本的draw的默认参数应该为Green(1)

  而结果是Red(0)

  为了更清楚的看一下究竟调用的是哪一个draw,我们多加一点打印信息。

clipboard

  ok,结果很清楚了,函数调用的版本是Rectangle*,但是默认参数调用的是Shape*中定义的。

  所以结论就是:这个函数调用由class Shape和class Rectangle class的draw声明式各出一半力。

  那么C++为什么有这么奇怪的设定呢 简单的说,就是:性能。运行期对参数动态绑定缺省值很慢很复杂,所以考虑到性能问题,并没有支持默认参数的动态绑定。

  小结:

  所以正如本节的标题,禁止重载一个继承而来的默认参数值,因为缺省参数值都是静态绑定,而使用这些默认参数的virtual函数却是动态绑定。

          

首页 上一页 4 5 6 7 下一页 尾页 7/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇libjingle线程机制 下一篇vc++教程之win7下基址定位处理

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·About - Redis (2025-12-26 08:20:56)
·Redis: A Comprehens (2025-12-26 08:20:53)
·Redis - The Real-ti (2025-12-26 08:20:50)
·Bash 脚本教程——Li (2025-12-26 07:53:35)
·实战篇!Linux shell (2025-12-26 07:53:32)