指向基类的指针与指向派生类的指针

ccwd002 2003-04-09 07:19:11
指向基类对象的指针能指向派生类对象,反过来能不能行?
...全文
158 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
messagebox 2003-04-17
  • 打赏
  • 举报
回复
当派生类指针指向基类对象:可以调用的成员函数有基类的成员函数,以及派生类中不涉及
派生类自已的数据成员的成员函数,这些调用都不会有问题
hxy_sss 2003-04-15
  • 打赏
  • 举报
回复
class A{};
class B:public A{};
A *pa=new B;
B *pb=dynamic_case<B*>(pa);
flysundy 2003-04-15
  • 打赏
  • 举报
回复
看来汇编也得学得好啊
oyd 2003-04-14
  • 打赏
  • 举报
回复
我想到了一个很容易理解的解释,是从逻辑上的。
例如,我们有"天体"这个基类,它的一个派生类是"恒星"。
而另外一个类"黑洞",有一个"吞噬"的方法,该方法接受所有的指向"天体"的指针,这就意味着,"星体"需要借助"天体"指针来传递。也就是说,指向基类的指针要能指向派生类。
一句话,黑洞吞噬所有的天体,而不管该天体实际上是行星还是恒星。
基于这样的理由,c++设计为指向基类对象的指针能指向派生类对象,而反过来则会出现错误
JustHere 2003-04-13
  • 打赏
  • 举报
回复
mark
pandix 2003-04-13
  • 打赏
  • 举报
回复
up
fengge8ylf 2003-04-13
  • 打赏
  • 举报
回复
学习
season11 2003-04-13
  • 打赏
  • 举报
回复
同意楼上
oyd 2003-04-11
  • 打赏
  • 举报
回复
学习
woxihuanbohe 2003-04-10
  • 打赏
  • 举报
回复
#include<iostream.h>
class a {
public:
int b;
virtual int get()
{
cout<<"a::get()";
return b;}
};
class c :public a
{
public:
int d;
int add()
{
return d+7;
}
int get()
{
cout<<"c::get()";
return d;}
};
/*void fun(a &www)//第一版本
{
cout<<www.get();
}*/
void fun( a www)//第二版本
{
cout<<www.get();
}
int main()
{
a *pa;
c cc;
pa=&cc;//很正常
cout<<pa->get()<<endl;//调用了c类的函数
c* pc;
a aa;
pc=(c*)&aa;//会出现数据丢失!即发生类的“切割”问题!这句强制转换
cout<<pc->get()<<endl;//调用了a类的函数
cout<<pc->add()<<endl;//此句会出错!?虽然pc是指向a对象,但是他却成功的调用了c中的add();这是为何?
fun(cc);//使用第一 版本就调用c,使用第二版本就调用a
fun(aa);//使用第一版本就调用a,使用第二版本就调用a
return 0;
}
//以上代码我用vc6编译!
//我觉得你举的例子好象也没证明是对象切割,更多的是关于一些多态。类的对象作为参数的传递到函数的时候会调用拷贝构造函数,对象切割更多的是发生在拷贝构造函数的调用中?
//请lifanxi(Byron)明示例!!
缘点星空 2003-04-10
  • 打赏
  • 举报
回复
虽然我语言学的不太好,不过在这里,却学到了不少东西!!
woxihuanbohe 2003-04-10
  • 打赏
  • 举报
回复
to: lifanxi(Byron)
非常感谢你对我这个问题的解释!纠正了我长时间以来的误解!
使我对多态,对象切割,拷贝构造的问题有了更深入的认识!

也非常感谢楼主提供这个机会!:)

lifanxi 2003-04-10
  • 打赏
  • 举报
回复
To woxihuanbohe(我喜欢):
谢谢您参与论讨。
对于您说的问题,我解释如下:
首先,我修改了您给出的程序,加了两个构造函数给成员变量赋了一下值,这样结果看起来可以清楚一些。
#include<iostream>
using namespace std;
class a
{
public:
a() : b(1234){}
int b;
virtual int get()
{
cout<<"a::get()";
return b;
}
};
class c :public a
{
public:
c() : d(2345){}
int d;
int add()
{
return d+7;
}
int get()
{
cout<<"c::get()";
return d;
}
};
/*void fun(a &www)//第一版本
{
cout<<www.get() << endl;
}*/
void fun(a www)//第二版本
{
cout<<www.get() << endl;
}
int main()
{
a *pa;
c cc;
pa=&cc;//很正常
cout<<pa->get()<<endl;//调用了c类的函数
c* pc;
a aa;
pc=(c*)&aa;//这句强制转换
cout<<pc->get()<<endl;//调用了a类的函数
cout<<pc->add()<<endl;//此句会出错!?虽然pc是指向a对象,但是他却成功的调用了c中的add();这是为何?
fun(cc);//使用第一 版本就调用c,使用第二版本就调用a
fun(aa);//使用第一版本就调用a,使用第二版本就调用a
return 0;
}
int main()中前6行是没有问题的。
第7行,pc = (c*) &aa;就是把强行把派生类的指针指向基类的对象,这是危险的操作,但不会发生对象切割,因为这只是修改了一个指针,并没有对类的对象去实施什么破坏性的操作。
45: pc=(c*)&aa;//这句强制转换
004016C8 lea eax,[ebp-1ch]
004016CB mov dword ptr [ebp-14h],eax
从汇编上看出,它只是把aa对象的地址ebp-1ch放到了pc所占的内存中ebp-14h中,没有动aa对象ebp-1ch附近的内容,所以不会数据丢失。
cout<<pc->get()<<endl;凑合着可以运行,因为get本身是a类的一个成员函数,因为它是虚的,所以这里调用的是a类的函数。
cout<<pc->add()<<endl;这也凑合着可以运行,因为虽然a类中没有add()函数,但因为c类中的add()不是虚的,所以这里做静态绑定,调用了c类的add()函数。因为add()函数中对一个a类中不存在成员d进行了加7操作,所以就返回了一个无效的数字。可以从输出结果中清楚的看到。
004016FD mov ecx,dword ptr [ebp-14h]
00401700 call @ILT+415(c::add) (004011a4)
从汇编上看出,这里没有用虚函数的机制,只是一个静态绑定。
如果把add也改成virtual的,这里就会做动态绑定
00401704 mov ecx,dword ptr [ebp-14h]
00401707 call dword ptr [edx+4]
结果就更离奇了,您可以自己试试。
对于这一点,为什么可以正常调用(但结果不对)非虚的add()函数,可以再参考我在另一个贴中的回复。
http://expert.csdn.net/Expert/topic/1414/1414072.xml?temp=.8994867
下面解释func:
第一版本是传引用,就相当于是传了指针,所以可以实现多态。func(cc)会调用c的get(),func(aa)会调a的get()。
004016DF lea ecx,[ebp-1Ch]
004016E2 push ecx
004016E3 call @ILT+350(fun) (00401163)
可看出,只用堆栈传了指针,没有挎贝对象。
第二版本是传对象,会调挎贝构造函数,这里没有定义,所以就进行了默认的挎贝。因为形参是基类a的,它不知道c又在它身上加了d这个成员,所以它就把一个c的对象挎贝成了a的对象,a中没有的成员d就被切割了。所以func(cc)和func(aa)都只会调a的成员函数get()。这种现象就是所谓的“对象切割”,经过挎贝后,c的对象被切割得只剩下a的部分了。
0040172C call @ILT+115(a::a) (00401078)
00401731 call @ILT+640(fun) (00401285)
很明显调了a的挎贝构造函数,对象被切割。
woxihuanbohe 2003-04-09
  • 打赏
  • 举报
回复
多谢 lifanxi(Byron) 的纠正!
freeleo 2003-04-09
  • 打赏
  • 举报
回复
来晚了,同意楼上
lifanxi 2003-04-09
  • 打赏
  • 举报
回复
>c* pc;
>a aa;
>pc=aa;//会出现数据丢失!即发生类的“切割”问题!
这编译通不过的。如果改成pc=(c*)&aa,的话,可以通过,但是是危险的操作。不过单这一句也不会数据丢失。这不是对象切割问题。
对象切割是指
void f(a aa)
{
//...
}
int main()
{
c cc;
f(cc);
return 0;
}
这样的情况。
woxihuanbohe 2003-04-09
  • 打赏
  • 举报
回复
是可以的 ,但是会出现一些麻烦!
比如
class a {
int b;
}
class c :public a
{
int c;
}

a *pa;
c cc;
pa=&cc;//很正常
c* pc;
a aa;
pc=aa;//会出现数据丢失!即发生类的“切割”问题!
liem 2003-04-09
  • 打赏
  • 举报
回复
基类的指针可以指向派生类的对象,但必须是public派生。
当多继承时,特别是有共同基类再进行派生时,如果不用虚基类,就不行了,
lifanxi 2003-04-09
  • 打赏
  • 举报
回复
比如A类有一个成员c,B类是从A类派生的,并加了一个成员d。
如果是A *pa去指B的对象,没问题,就是B的新加的成员访问不到而已。
反之用B *pb支指A的对象,pb以为它指是的B对象,有一个d成员,但实际上指的是A的对象,没有d这个成员,如果试图去访问d,就出错了。
lifanxi 2003-04-09
  • 打赏
  • 举报
回复
不可以。虽然可能通过强制类型转换来实现,但那将是很危险的。

64,682

社区成员

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

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