您的位置:首页 > 编程语言 > C语言/C++

Effective C++笔记: 继承和面向对象设计(二)

2009-07-17 18:01 281 查看
 

Item 34: 区分 inheritance of interface(接口继承)和 inheritance of implementation(实现继承)
Public继承由2部分组成: inheritance of function interfaces(函数接口的继承)和 inheritance of function implementations(函数实现的继承)。
 
Base class的成员函数可以声明为三种类型:pure virtual, impure virtual 和 non virtual.
为了更好地感觉这些选择之间的不同之处,考虑一个在图形应用程序中表示几何图形的 class hierarchy(类继承体系):
class Shape {
public:
  virtual void draw() const = 0;
  virtual void error(const std::string& msg);
  int objectID() const;
  ...
};
class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };
pure virtual: pure virtual functions(纯虚拟函数)的两个最显著的特性是它们必须被任何继承它们的具体类重新声明,和抽象类中一般没有它们的定义。
声明一个 pure virtual function(纯虚拟函数)的目的是使 derived classes 只继承一个函数接口。
为一个 pure virtual function(纯虚拟函数)提供一个定义是有可能的。也就是说,你可以为 Shape::draw 提供一个实现,而 C++ 也不会抱怨什么,但是调用它的唯一方法是用 class name 限定修饰这个调用:
Shape *ps = new Shape;              // error! Shape is abstract
Shape *ps1 = new Rectangle;         // fine
ps1->draw();                     // calls Rectangle::draw
Shape *ps2 = new Ellipse;           // fine
ps2->draw();                     // calls Ellipse::draw
ps1->Shape::draw();                 // calls Shape::draw
ps2->Shape::draw();                 // calls Shape::draw
为一个纯虚函数提供定义,可以实现一种机制:为impure virtual函数提供更平常更安全的缺省实现.
impure virtual:声明一个 simple virtual function 的目的是让 derived classes 继承该函数接口和一个缺省实现。Derived class可以使用该缺省实现,也可以定义自己独有的行为。
我们可以用以下方法来强迫子类明确的表示自己是使用缺省行为,还是定义一个新的行为:
class Airplane {
public:
  virtual void fly(const Airport& destination) = 0;
  ...
};
void Airplane::fly(const Airport& destination)   // an implementation of
{                                           // a pure virtual function
  default code for flying an airplane to
  the given destination
}
class ModelA: public Airplane {
public:
  virtual void fly(const Airport& destination)
  { Airplane::fly(destination); }
  ...
};
class ModelB: public Airplane {
public:
  virtual void fly(const Airport& destination)
  { Airplane::fly(destination); }
  ...
};
class ModelC: public Airplane {
public:
  virtual void fly(const Airport& destination);
  ...
};
void ModelC::fly(const Airport& destination)
{
  code for flying a ModelC airplane to the given destination
}
本质上,fly 可以被拆成两个基本组件。它的 declaration(声明)指定了它的 interface(接口)(这是 derived classes(派生类)必须使用的),而它的 definition(定义)指定它的缺省行为(这是 derived classes(派生类)可以使用的,但只是在他们明确要求这一点时)。
 
non-virtual:
声明一个 non-virtual function(非虚拟函数)的目的是使派生类既继承一个函数的接口,又继承一个强制的实现,derived class绝不该去重新定义non virtual函数。
你可以这样考虑 Shape::objectID 的声明,“每一个 Shape object 有一个产生 object identifier(对象标识码),而且这个 object identifier(对象标识码)总是用同样的方法计算出来的,这个方法是由 Shape::objectID 的定义决定的,而且 derived class(派生类)不应该试图改变它的做法。”
总结:
Inheritance of interface(接口继承)与 inheritance of implementation(实现继承)不同。在 public inheritance(公开继承)下,derived classes(派生类)总是继承 base class interfaces(基类接口)。
Pure virtual functions(纯虚拟函数)指定 inheritance of interface only(仅有接口被继承)。
Simple (impure) virtual functions(简单虚拟函数)指定 inheritance of interface(接口继承)加上 inheritance of a default implementation(缺省实现继承)。
Non-virtual functions(非虚拟函数)指定 inheritance of interface(接口继承)加上 inheritance of a mandatory implementation(强制实现继承)。
 
Item 35: 考虑virtual functions(虚拟函数)之外的其他选择
总结:
Virtual函数的替代方案包括NVI手法以及Strategy设计模式的多种形式。NVI手法自身是一个特殊形式的Template Method设计模式。
将技能从成员函数移到class外部函数,带来的一个缺点是,非成员函数无法访问class的non public成员。
tr1::fuction对象的行为就像一般函数指针。
 
Item 36: 绝不要重定义一个 inherited non-virtual function(通过继承得到的非虚拟函数)
class 非常重要的一条,在item 35中也有提及。
绝对不要重新定义继承而来的non virtual函数。
 
Item 37:绝不重新定义通过继承得到的缺省参数值
virtual functions(虚拟函数)是 dynamically bound(动态绑定),而 default parameter values(缺省参数值)是 statically bound(静态绑定)。
一个 object(对象)的 static type(静态类型)就是你在程序文本中声明给它的 type(类型)。考虑这个 class hierarchy(类继承体系):
// a class for geometric shapes
class Shape {
public:
  enum ShapeColor { Red, Green, Blue };
  // all shapes must offer a function to draw themselves
  virtual void draw(ShapeColor color = Red) const = 0;
  ...
};
class Rectangle: public Shape {
public:
  // notice the different default parameter value — bad!
  virtual void draw(ShapeColor color = Green) const;
  ...
};
class Circle: public Shape {
public:
  virtual void draw(ShapeColor color) const;
  ...
};
现在考虑这些 pointers(指针):
Shape *ps;                       // static type = Shape*
Shape *pc = new Circle;          // static type = Shape*
Shape *pr = new Rectangle;       // static type = Shape*
dynamic types(动态类型),就像它的名字所暗示的,能在程序运行中变化,特别是通过 assignments(赋值):
ps = pc;                       // ps's dynamic type is
                               // now Circle*
ps = pr;                       // ps's dynamic type is
                               // now Rectangle*
virtual functions(虚拟函数)是 dynamically bound(动态绑定),意味着被调用的特定函数取决于被用来调用它的那个 object(对象)的 dynamic type(动态类型):
pc->draw(Shape::Red);             // calls Circle::draw(Shape::Red)
pr->draw(Shape::Red);             // calls Rectangle::draw(Shape::Red)
但是,当你考虑 virtual functions with default parameter values(带有缺省参数值的虚拟函数)时,就全乱了套,因为,如上所述,virtual functions(虚拟函数)是 dynamically bound(动态绑定),但 default parameters(缺省参数)是 statically bound(静态绑定)。这就意味着你最终调用了一个定义在 derived class(派生类)中的 virtual function(虚拟函数)却使用了一个来自 base class(基类)的 default parameter value(缺省参数值)。
pr->draw();                // calls Rectangle::draw(Shape::Red)!
在此情况下,pr 的 dynamic type(动态类型)是 Rectangle*,所以正像你所希望的,Rectangle 的 virtual function(虚拟函数)被调用。在 Rectangle::draw 中,default parameter value(缺省参数值)是 Green。然而,因为 pr 的 static type(静态类型)是 Shape*,这个函数调用的 default parameter value(缺省参数值)是从 Shape class 中取得的,而不是 Rectangle class!
为什么 C++ 要坚持按照这种不正常的方式动作?答案是为了运行时效率。如果 default parameter values(缺省参数值)是 dynamically bound(动态绑定),compilers(编译器)就必须提供一种方法在运行时确定 virtual functions(虚拟函数)的 parameters(参数)的 default value(s)(缺省值),这比目前在编译期确定它们的机制更慢而且更复杂。
 
总结:
绝不要重定义一个 inherited default parameter value(通过继承得到的缺省参数值),因为 default parameter value(缺省参数值)是 statically bound(静态绑定),而 virtual functions ——你唯一应该覆写的东西——是 dynamically bound(动态绑定)。
 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息