父类指针转换为子类指针与父类对象转换为子类对象的疑惑

amao666 2008-10-14 01:49:45
如题:如下代码:
class A{}; //父类;
class B :public A{}; //子类;
A a; //父类对象,已初始化;
B b; //子类对象,已初始化;
A *pa; //父类指针,已初始化;
B *pb; //子类指针,已初始化;


问题:
1. 为什么父类对象不能转换为子类对象而父类指针可以强制转换为子类指针?
如:pb=(pb*)pa; //合法;他们指向的内存模型和内存大小都不一样为何能转换,小->大
b=a;或者b=(b)a; //非法;

2. 为什么父类指针转换为子类指针需要显式转换而子类指针转换为父类指针不用显式转换?
如:pa=pb; //合法;
pb=pa; //非法;
pb=(pb*)pa; //合法;

希望各位大哥大姐帮帮忙,初学者的疑惑,不知你们初学时有没有遇到这样的疑惑,希望能深入的给我个详细的解释,非常谢谢!


...全文
1338 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
amao666 2008-10-16
  • 打赏
  • 举报
回复
非常感谢jjzhk耐心的讲解!也很感动!感动你的无私指教!同时也感谢楼上其它几位的见解!要结贴了!
JJZHK 2008-10-15
  • 打赏
  • 举报
回复
呵呵。amao666说的很正确。
1.两个指针的复制操作是不发生裁剪的。只是发生指针访问空间的改变。
pb = (pb*)pa,这个是合法的,相当于pa的访问空间变大了,然后付给了pb,这样,原来属于A的没有问题,但是空间变大以后,属于B的部分并没有进行必要的实例化操作,如果你此时用pb去访问属于B的部分,那肯定有问题。
2.裁剪发生在内存模型大的类型向内存模型小的类型转换时,这个你说的没错,是我发的有问题。如果你定义了operator=(const A& a)的话,a = b,是会发生裁剪的。因为b的内存模型大于a的内存模型。
3.关于A * a = new B()吧,但是不能B * b = new A(),从逻辑上讲,B对象是一个A对象,但A对象不一定是一个B对象,就像学生是人,但人不一定是学生。一样的。从程序上讲,你可以参照我的1. new A()实例化了A的部分,不会实例化B的部分,因此b访问B的部分的时候一定出错。
4.B * b = (B *)new A(),这个是合法的,但是不可行吧。参照3.

不知道是否解释清楚?
amao666 2008-10-14
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 jjzhk 的回复:]
1.pb=(pb*)pa; 合法是因为这只是指针的转化,也就是指针指向的转变,相当于你可以把一个int*强转成void*或者什么其他的*。但是这样做会发生裁剪。而b=a,参照luansxx的答案。
2.pa=pb; //合法; 子类对象可以赋值给基类对象
pb=pa; //非法; 反之,不可以
pb=(pb*)pa; //合法; 发生裁剪

就像我们可以A a = new B(),但是不能B b = new A(),是一样的。
[/Quote]


这位大哥,据我所了解,在内存模型大的类型向内存模型小的类型转换时才发生裁剪,父类指针pa指向的内存模型一般情况小都小于子类指针pb所指向的内存模型,你说“pb=(pb*)pa; //合法; 发生裁剪” ,应该不会发生裁剪吧,希望你能再介绍详细点,谢谢!
还有你说"A a = new B(),但是不能B b = new A(),是一样的", 你笔误应该是A * a = new B()吧,但是不能B * b = new A(),为什么?还是那问题啊,还是没搞清楚,仍是是B * b = (B *)new A(),合法也可行!为什么???
tianjiao85 2008-10-14
  • 打赏
  • 举报
回复
从对象和指针的区别想想:
对象构建在内存中分配对象所需要的大小,父类对象和子类对象成员不同,内存布局肯定不同。
指针是指向对象的内存地址,不管是什么类型,都是一个地址值,4位 ,而对于指向类的对象的指针,一般指向的是某一个起始东东(不清楚了),
子类是在父类的基础上加上自己的成员,所以起始东东是一样的。子类指针指向的和父类指针指向的是相同的。。
他们间的转换,加于不加,也只是编译器要求,总之他们间的转换,不会改变他们所指向的对象。
而父子类对象间赋值,直接是把公共的成员拷贝过去,
父类赋值给子类时候,父类小于子类,即使加强制符号,子类独有的成员也是不确定的,所以不行。
das_jack 2008-10-14
  • 打赏
  • 举报
回复
看看c++对象模型就更清楚了
JJZHK 2008-10-14
  • 打赏
  • 举报
回复
to chenzhp,你很适合当领导啊,总结性发言
chenzhp 2008-10-14
  • 打赏
  • 举报
回复
两位说的很好!
JJZHK 2008-10-14
  • 打赏
  • 举报
回复
1.pb=(pb*)pa; 合法是因为这只是指针的转化,也就是指针指向的转变,相当于你可以把一个int*强转成void*或者什么其他的*。但是这样做会发生裁剪。而b=a,参照luansxx的答案。
2.pa=pb; //合法; 子类对象可以赋值给基类对象
pb=pa; //非法; 反之,不可以
pb=(pb*)pa; //合法; 发生裁剪

就像我们可以A a = new B(),但是不能B b = new A(),是一样的。
Fighting Horse 2008-10-14
  • 打赏
  • 举报
回复
合法不合法是对默认情况而言的,如果重载赋值运算符=,一切都可能变得合法,当然,指针的强制转换即使错误编译器也不能阻止你。
至于默认为什么b=a不行,那是因为通过基类给子类赋值,子类的一些字段可能没有取值的根据,编译器只好报错了。
通过子类给基类赋值,编译器构建的默认赋值运算只是做二进制拷贝。
holetboy 2008-10-14
  • 打赏
  • 举报
回复
楼主很有建设性吗,呵呵,俺也想知道!帮顶了!!

65,211

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧