5.5赋值兼容规则 赋值兼容规则是指在公有派生情况下,一个派生类的对象可用于基类的对象可以使用的 地方。 例如:如果类 derived从base类公有派生,则赋值兼容规则指的是下面三种情况 (1)派生类的对象可以赋给基类的对象 derived d base b b=d ∥对象d中所含的b类成员被赋给b (2)派生类的对象可以初始化基类的引用 derived d (3)派生类的对象的地址可以赋给指向基类的指针 derived d base"pb&d 注意:通过pb或br只能访问对象d中所继承的基类的成员。 下面以指针为例,分析一下单一继承和多重继承中使用该规则的意义和应注意的问题 5.5.1单一继承的情况 继承为代码重用提供了一种简便的方法,无需修改基类的源代码,可以通过从基类派生 派生一个新类来裁剪基类,以满足具体的应用要求,达到代码重用的目的。赋值兼容性规则 拓展了代码重用的广度。 例EX15.CPP可以求出屏幕上一个圆和一个椭圆之间的距离。 5.5.2多重继承的情况 多重继承可以视为多个单一继承的组合,因此,赋值兼容规则也适用于多重继承的情况。 设某多重继承的有向无环图(DAG)表示为图5-1 图5-1 并且C类是从A类和B类公有派生,则C类的对象可以用于A类或B类的对象可以使用 的地方,例如: Cc: B°pb=&c 并且可以使用强制类型转换将pa或pb置给指向C类对象的指针。如: C°pc=(c*)par ase ase基类又有共同的基类时,将指向派生类的指针强制为指向该共同基类 例如,对于图5-2的DAG basel base2 derived 图5-2
5.5 赋值兼容规则 赋值兼容规则是指在公有派生情况下,一个派生类的对象可用于基类的对象可以使用的 地方。 例如:如果类 derived 从 base 类公有派生,则赋值兼容规则指的是下面三种情况。 (1)派生类的对象可以赋给基类的对象。 derived d; base b; b=d; //对象 d 中所含的 b 类成员被赋给 b (2)派生类的对象可以初始化基类的引用。 derived d; base& br=d; (3)派生类的对象的地址可以赋给指向基类的指针。 derived d; base *pb=&d; 注意:通过 pb 或 br 只能访问对象 d 中所继承的基类的成员。 下面以指针为例,分析一下单一继承和多重继承中使用该规则的意义和应注意的问题。 5.5.1 单一继承的情况 继承为代码重用提供了一种简便的方法,无需修改基类的源代码,可以通过从基类派生 派生一个新类来裁剪基类,以满足具体的应用要求,达到代码重用的目的。赋值兼容性规则 拓展了代码重用的广度。 例 EX_15.CPP 可以求出屏幕上一个圆和一个椭圆之间的距离。 5.5.2 多重继承的情况 多重继承可以视为多个单一继承的组合,因此,赋值兼容规则也适用于多重继承的情况。 设某多重继承的有向无环图(DAG)表示为图 5—1: 并且 C 类是从 A 类和 B 类公有派生,则 C 类的对象可以用于 A 类或 B 类的对象可以使用 的地方,例如: C c; A *pa=&c; B *pb=&c; 并且可以使用强制类型转换将 pa 或 pb 置给指向 C 类对象的指针。如: C *pc=(c *)pa; 但是,在派生类的基类又有共同的基类时,将指向派生类的指针强制为指向该共同基类 的指针时会产生二义性。例如,对于图 5—2 的 DAG:
分析下面的程序: derived d derived*dptr=&d ror b=(base*dptr;∥eror 当试图将一个指向 derived类的指针强制转换成指向base类的指针时,编译器不知道是 通过 basel还是base2继承路径。为避免这种二义性,使用下面的语句: b=(base* basel*)dptr;∥显示指明全路径 b=(basel*)di ∥强制到 basel*,然后从 basel*到base*进行隐式类型转换 上面的情况同样适用于函数调用中参数传递和返回值的情况。 多重继承比单一继承情况复杂的原因在于,派生类在这两种情况下在内存中的数据成员 布局不一样。先分析单一继承情况下数据成员在内存中的布局。设有 class bas }; class derived: public base float d 则派生类 derived的一个对象在内存中的布局如图5-3所示。 A b float d 图5-3单一继承情况下派生类的对象的内存布局 分析下面的程序: derived d base *bptr=&d/bptr指向图5-3中箭头A所指向的位置 derived*dptr=&d;/dptr指向图5-3中箭头A所指向的位置 如果派生类 derived由下面的类等级建立: class base int b class basel public base int bl class base 2: public base
分析下面的程序: derived d; derived *dptr=&d; base *b; b=dptr; //error b=(base *)dptr; //error 当试图将一个指向 derived 类的指针强制转换成指向 base 类的指针时,编译器不知道是 通过 base1 还是 base2 继承路径。为避免这种二义性,使用下面的语句: b=(base *)(base1 *)dptr;//显示指明全路径 b=(base1 *)dptr; //强制到 base1 *,然后从 base1 *到 base *进行隐式类型转换 上面的情况同样适用于函数调用中参数传递和返回值的情况。 多重继承比单一继承情况复杂的原因在于,派生类在这两种情况下在内存中的数据成员 布局不一样。先分析单一继承情况下数据成员在内存中的布局。设有 class base { int b; }; class derived:public base { float d; } 则派生类 derived 的一个对象在内存中的布局如图 5—3 所示。 分析下面的程序: derived d; base *bptr=&d;//bptr 指向图 5—3 中箭头 A 所指向的位置 derived *dptr=&d;//dptr 指向图 5—3 中箭头 A 所指向的位置 如果派生类 derived 由下面的类等级建立: class base { int b; }; class base1:public base { int b1; }; class base2:public base
void f20 class derived: public basel, public base2 float d 则派生类 derived的一个对象在内存中的布局如图5-4所示。 A→:in+1 base basel int b1 derived base2 图5-4多重继承情况下派生类的对象的内存布局 和单一继承情况作比较,A位置由 derived*类型和base*类型的指针指向,而B位置 由base2*类型的指针指向,base2类的数据成员不再从开始位置A存放 将 derived类的对象d的地址置给 basel*类型和base2*类型的指针时会产生不同的效 果,即 base l* bptr I=&d;/&d指向A位置,但 bptrI指向A位置 base2*bpt2=&d;∥&d指向A位置,但 bptr I指向B位置,编译器将调整&d的值为 /l(char *)&d+sizeof(basel) 反过来,从指向 basel类或bae2类的指针转换成指向对应的base类的指针时,可分别 指向A位置和B位置,但从 derived*强制转换到base*时必须指定路径,否则会产生二义 性 一个base*类型的指针也不能直接转换成 derived*类型的指针: derived*dptr dptr( derived*)bptr/ero,不知道转换路径 dpt=( derived*)( basel*)bptr;/ correct, bptr的值不作调整 dpt=( derived*)(base*)bptr;/ orrect, bptr的值要调整为指向A的位置
{ int b2; public: void f2(); }; class derived:public base1,public base2 { floadt d; }; 则派生类 derived 的一个对象在内存中的布局如图 5—4 所示。 和单一继承情况作比较,A 位置由 deerived *类型和 base *类型的指针指向,而 B 位置 由 base2 *类型的指针指向,base2 类的数据成员不再从开始位置 A 存放。 将 derived 类的对象 d 的地址置给 base1 *类型和 base2 *类型的指针时会产生不同的效 果,即 base1 *bptr1=&d;//&d 指向 A 位置,但 bptr1 指向 A 位置 base2 *bptr2=&d;// &d 指向 A 位置,但 bptr1 指向 B 位置,编译器将调整&d 的值为 //(char *)&d+sizeof(base1) 反过来,从指向 base1 类或 base2 类的指针转换成指向对应的 base 类的指针时,可分别 指向 A 位置和 B 位置,但从 derived *强制转换到 base *时必须指定路径,否则会产生二义 性。 一个 base *类型的指针也不能直接转换成 derived *类型的指针: base *bptr; derived *dptr; dptr=(derived *)bptr;//error,不知道转换路径 dptr=(derived *)(base1 *)bptr;//correct,bptr 的值不作调整 dptr=(derived *)(base2 *)bptr;//correct,bptr 的值要调整为指向 A 的位置