派生类转换为基类,hierarchy向上走,称为upcasting;基类转换为派生类,hierarchy向下走,称为downcasting。downcasting是不安全的,一般不被允许。
含有虚函数的类,编译器给每个对象添加一个隐藏成员,该成员保存一个指向函数地址数组的指针。这个函数地址数组包含了类的所有虚函数地址。如果派生类覆盖了基类的虚函数,那么这个数组中相应的元素也被覆盖;如果派生类增加了一个虚函数,那么会在数组中增加一个元素。不管虚函数数目是1还是10,都只在对象中添加一个地址成员,只是表的大小不同而已。
返回类型协变
覆盖要求函数具有完全相同的入参。
一般覆盖具有相同的返回值,否则会提示错误
virtual
double
area ()
const
=
0
;
virtual float area () const ; // 编译器提示错误,返回类型不同
virtual float area () const ; // 编译器提示错误,返回类型不同
这个规则对返回类型协变而言,则有所放松。覆盖的返回值不区分基类或派生类。从语意上理解,一个派生类也是一个基类。如下:
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引用(或指针)。
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.协变返回机制将我们从这样的一种处境解脱出来:不得不使用易于出错的转换操作来“重新”提供类型信息,而这种信息是一开始就不应该丢掉的:(那么,对于友元,派生的operator+,怎么样调用基类的operator+呢?)
Shape
*
s
=
getACircleOrOtherShape ();
Const ShapeEditor & sed = s -> getEditor();
Ciecle * c = getACircle();
Const CircleEditor & ced = c -> getEditor();
Const ShapeEditor & sed = s -> getEditor();
Ciecle * c = getACircle();
Const CircleEditor & ced = c -> getEditor();