一般来说,一个重写的函数与被它重写的函数必须具有相同的返回类型:
Class Shape {
Public:
//…
Virtual double area () const = 0;
//…
};
Class Circle : public Shape {
Public:
//…
Float area () const ; //错误!返回类型不同
//…
};
然而,这个规则对于”协变返回类型(covariant return type)”的情形来说有所放松.也就是说,若B是一个类类型,并且一个基类虚拟函数返回B *,那么一个重写的派生类函数可以返回D *,其中的D公有派生于B(即D是一个(is-a)B).若基类虚函数返回B &,那么一个重写的派生类函数可以返回一个D&.考虑如下一个shape层次结构的clone操作:
Class Shape {
Public:
//…
Virtual Shape *clone () const = 0; //prototype(原型)
//…
};
Class Circle : public Shape {
Public:
//…
Circle *clone () const ;
//…
};
重写的派生类函数被声明为返回一个Circle *而不是一个Shape *.这是合法的,因为Circle是一个Shape.注意如一个CIrcle被当作Shape进行操作,从返回的Circle *就回被自动转化为Shape *:
Shape *s1 = getACircleOrOtherShape ();
Shape *s2 = s1->clone();
当直接操纵派生类型而不是通过其基类接口来操纵它们时,使用协变返回类型的优势就会体现出来了:
Circle *c1 = getACircle();
Circle 8c2 = c1->clone();
如果没有协变返回机制,Circle::clone将不得不精确的匹配Shape::clone的返回类型,从而返回一个Shape *.我们就被迫将返回结果转换为Circle *.
Circle *c1 = getACircle();
Circle *c2 = static_cast<Ciecle *>( c1->clone());
再看另外一个例子。考虑如下Shape的Factory Method成员,它返回一个引用,指向与具体的形状对应的形状编辑器:
Class ShapeEditor {……};
Class Shape {
Public:
//…
virtual const ShapeEditor & getEditor () const = 0; //Factory Method
//…
};
//….
Class Circle;
Class CircleEditor : public ShapeEditor { … };
Class Circle : Public Shape{
Public:
Const CircleEditor &getEditor () const ;
//…
};
在这个例子中,注意CircleEditor必须在Circle::getEditor的声明之前被完整地定义(而不能仅仅声明)。因为编译器必须知道CircleEditor对象的布局,才能执行适当的地址操纵,从而将一个CircleEditor引用(或指针)转换为一个ShapeEditor引用(或指针)。
协变返回类型的优势在于,总是可以在适当程度的抽象层面工作。若我们是处理Shape,将获得一个抽象的ShapeEditor;若正在处理某种具体的形状类型,比如Circle,我们就可以直接获得CiecleEditor.协变返回机制将我们从这样的一种处境解脱出来:不得不使用易于出错的转换操作来“重新”提供类型信息,而这种信息是一开始就不应该丢掉的:
Shape * s = getACircleOrOtherShape ();
Const ShapeEditor &sed = s->getEditor();
Ciecle *c =getACircle();
Const CircleEditor &ced = c->getEditor();