[转载]C++学习第一部分:继承(三)

标签:
转载 |
分类: 计算机相关 |
类型兼容规则
l
–
–
–
l
l
l
class Base
};
class Derived : public Base
};
void main( void )
{
}
这还意味着:1子类对象赋值给父类对象时,父类对象的“表面类型”和“真实类型”是一致的。 会出现真切割,2和 3则会出现指针(或引用)的“表面类型”和它所指向的对象的“真实类型”的不一致现象,是假切割。这种貌合神离恰是动态多态的魅力所在,也给人带来了困惑和理解的难度。
于是就有“静态类型”“动态类型”之说。
多层继承后,尽管用指针指向各类对象,但“切割”作用使子类的同名函数仍不能被调用。
#include <iostream.h>
#include “conio.h“ //用于提供getch() 函数
class k
{
};
class kk: public k
{
};
class xq: public kk
{
public:
};
void main()
{
}
运行结果:
进入主函数!!!!!!!!!!!!
进入K 类!10
进入K 类!105
进入K 类!10
注:C++中所有new出来的对象都要赋给指针,
P->at(10)这个操作就的实质是(*p).at(10),指针指向了堆中的对象,无名但是有地址。
因为p是基类类型的指针,表面类型是子类,因此造成了假切割,即调用的方法是继承下来的基类部分的方法。105是i的ASCII码值,第三个10是把10.88截断了,造成了失真。
类型兼容千万不要用在数组上
子类对象确实是父类对象的一种。但子类对象的数组却不是父类对象数组的一种。
D
B
此时可以用Array[i] 访问每个元素,可是能用 p[i]访问吗?Why?
如果用数组来访问,则Array这个名称相当于是指针,可以挨个访问D的对象。但是如果用p来访问,因为p是B类型的,它默认的一个“步长”是B类型对象的一个大小,也就是p的步长仅仅是D类对象中的B类部分(橘黄色区域),如果直接用p++,跨出的一步会仍包含前一个D类对象中自己新增的部分(蓝色区域),这样访问下去会造成混乱,如果一旦再发生值的修改,就可能造成内存垃圾甚至系统崩溃。
类型兼容也不要用在二级指针上
用父类的指针指向子类对象确实是一种常用的手法:
但指向父类指针的指针却不是指向子类指针的指针的一种:
circle类型指针的指针 != shape类型指针的指针
ccp指向cp,cp指向circle对象,则ccp是指向circle类型的指针的指针;
ssp是指向shape类型指针的指针。
多继承时的对象指针
class B1
};
class B2
};
class D :public
B1,
public B2
};
D
B1 * pb1 = pd;//预定义转换
B2 * pb2 = pd;//预定义转换
上图给出了子类对象两种可能的布局。尽管直观看起来指针指的位置不同,但是用户在使用上并无差别,即:
比较结果都是 true.三个指针都指向同一个对象。(不能进行
如果用输出语句显示这三个指针的地址,都是一样的,实际上其内幕是: 多继承时编译器记下了各个父类在子类对象中的偏移量,上一页所谓” 预定义的转换”就是加或减去偏移量.
由此我们可以得出结论——也是重要经验:
在使用指向对象的指针或引用时,千万不要丢失了类型,类型名在这里起着关键的作用。一旦将其交给了void *,则类型信息尽失,出错就是必然的。