在大多数情况下,当一个对象出现并开始工作时,它就能够执行我们需要它执行的事情,因为它的能力已经在接口中被明确的定义好了。
class Shape {
public:
virtual ~Shape();
virtual void draw() const = 0;
// ....
};
//````
Shape* s = getSomeShape(); // 获得一个shape,并且叫它去....
s->draw(); // 干活....
这样,我们只管叫这个对象去干活就行了。这很简单,也很高效,正是我们想要的效果。
然而,在实际中并非一切都是这么顺理成章,很多时候我们需要知道(或者测试)一个对象具备什么样的能力。
例如,对于上面的例子我们可能需要一些能够滚动的形状。那么我们写一个Rollable类,然后让具体的Shape类(例如下面的Circle类)多重继承自Shape和Rollable。
class Rollable{
public:
virtual ~Rollable();
virtual void roll() = 0;
};
像这样的类通常是一个接口类(interface class),因为它只指定了接口,如同Java的接口一样。通常来说,这样的类没有非静态成员,没有声明构造函数。一个虚析构函数和一个纯虚函数指明了一个Rollable对象能够做什么。
class Circle : public Shape, public Rollable { // 多重继承,圆形可以滚动
public:
//```
void draw() const;
void roll();
};
当然,并不是所有的Shape类对象都可以滚动
class Square : public Shape { // 单继承,正方形不能滚动
//...
void draw() const;
};
也有一些能滚动的对象却不是一个(not is-a)Shape.
class Wheel : public Rollable {
// ....
};
在我们试图滚动某一个对象之前,我们总是想知道面对的对象是否为Rollable,但是我们并不总是知道这样的对象是否具有这样的能力。在这种情况下,我们需要执行一个能力查询(测试)。在C++中能力查询通常是通过对“不相关”的类型进行dynamic_cast 转换而表达的。
Shape *s = getSomeShape();
Rollable *Roller = dynamic_cast<Rollable*>(s);
这种 dynamic_cast 用法通常称为“横向转型(cross-cast)”,因为它试图在一个类层次结构中执行横向转换,而不是向上或者向下转换。
如果查询成功比如说s实际指向的是圆(或者派生自Rollable的其它Shape),那么转型成功,Roller指向该对象。如果s实际指向的对象是一个Square(或者其它的未派生自Rollable的Shape), dynamic_cast 就会失败(结果返回一个空指针)。
所以可以用以下的代码来进行判断控制。
if ( Rollable *Roller = dynamic_cast<Rollable*>(s) )
roller->roll();
能力查询只是偶尔需要,但它们往往被过度使用。它们通常是糟糕设计的“指示器”。
参考:《C++必知必会》P72 条款27 能力查询