请教虚函数相关问题。

shwarpine 2010-12-24 11:15:34
class Base1 {

public:

virtual void f() { cout << "Base1::f" << endl; }

};

class Base2 {

public:

virtual void f() { cout << "Base2::f" << endl; }

};

class Base3 {

public:

virtual void f() { cout << "Base3::f" << endl; }

};

class Derive:public Base1,public Base2,public Base3 {
void f1() { cout << "Derive::f1" << endl; }
};


Derive d;

Base1 *b1 = &d;//输出:Base1::f

Base2 *b2 = &d;//输出:Base2::f

Base3 *b3=&d;//输出:Base3::f

Base3 *b33 = reinterpret_cast<Base3*>(&d);//为什么会输出:Base1::f??



而将Derive改成
class Derive:public Base1,public Base2,public Base3 {
void f() { cout << "Derive::f1" << endl; }
};
以上语句就都输出为Derive::f1,为什么?
谢谢解答。
...全文
318 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
xudashu123 2010-12-24
  • 打赏
  • 举报
回复
还有一个需要补充,第一个那个好想要用虚基类才行啊。
xudashu123 2010-12-24
  • 打赏
  • 举报
回复
Base1 *b1 = &d;//输出:Base1::f

Base2 *b2 = &d;//输出:Base2::f

Base3 *b3=&d;//输出:Base3::f

Base3 *b33 = reinterpret_cast<Base3*>(&d);//为什么会输出:Base1::f??

这个是派生类的对象转换成基类的对象
后一个是虚函数啊
Meteor_Code 2010-12-24
  • 打赏
  • 举报
回复
虚函数用指针调用,使用->操作符号
pengzhixi 2010-12-24
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 drhydrag 的回复:]
Base3 *b33 = reinterpret_cast<Base3*>(&d);//为什么会输出:Base1::f??
[/Quote]

只是说将d对象当成Base3来看待,但是实际上未调整d对象里面任何东西,所以调用哪f()都是有可能的,在于一个编译器的查找顺序。
食财物权情性 2010-12-24
  • 打赏
  • 举报
回复
Base3 *b33 = reinterpret_cast<Base3*>(&d);//为什么会输出:Base1::f??

这么神奇,能输出Base1::f
LZ再看看。
DrHydraG 2010-12-24
  • 打赏
  • 举报
回复
Base3 *b33 = reinterpret_cast<Base3*>(&d);//为什么会输出:Base1::f??


这是真的么?不会吧
就想叫yoko 2010-12-24
  • 打赏
  • 举报
回复
你的意思是每次都用对象去调用f函数吧

最后那个问题
你子类实现了基类的虚函数
基类指针指向子类对象后, 调用该函数当然是调用子类的那个函数啊
这时最标准的多态实现嘛
xy_zhang 2010-12-24
  • 打赏
  • 举报
回复
而将Derive改成
class Derive:public Base1,public Base2,public Base3 {
void f() { cout << "Derive::f1" << endl; }
};
以上语句就都输出为Derive::f1,为什么?
谢谢解答。

override
pengzhixi 2010-12-24
  • 打赏
  • 举报
回复
说实话,我都没看明白你的意思
liangjingmenghua 2010-12-24
  • 打赏
  • 举报
回复
关于Base3 *b33 = reinterpret_cast<Base3*>(&d), 首先,你需要了解对象d的内存布局。
按照你的例子,对象d只包含一个指向虚函数表的指针,该虚函数表里面,按照继承的顺序,分别放着base1::f, base2::f, base3::f的地址。
然后,你需要了解reinterpret_cast的意思, 你去网上查一下就知道了;编译器在看到类型匹配后,就按位拷贝所需的字节(这个例子是虚函数表),而且是按顺序拷贝,所以会把对象d的虚函数表的第一个函数指针base1::f拷贝到b33指向的经过转换后的对象,所以结果是base1::f被输出。

很简单,你要是还不明白,你可以把所有的virtual 去掉,这个时候就是base3::f被输出了。
因为没有virtual之后就没有了虚函数表,对象d就会被编译器正确的转化到base3类型了。
shwarpine 2010-12-24
  • 打赏
  • 举报
回复
谢谢你们。
pengzhixi 2010-12-24
  • 打赏
  • 举报
回复
调整指针指向子对象部分的起始地址。仅此而已。
shwarpine 2010-12-24
  • 打赏
  • 举报
回复
&d的值是0X0012FF74,
怎么一运行
Base1 *b1 = &d;

Base2 *b2 = &d;

Base3 *b3=&d;

b1就是0X0012FF74,
而b2是0X0012FF78,b3是0X0012FF7C了,为什么啊。


不应该都是0X0012FF74吗。

最后问下哈,我要多看书了看来~~~谢谢谢谢。
[Quote=引用 31 楼 pengzhixi 的回复:]

强制转换了,调整指针的指向是正常的,这就是为什么。
[/Quote]
pengzhixi 2010-12-24
  • 打赏
  • 举报
回复
强制转换了,调整指针的指向是正常的,这就是为什么。
shwarpine 2010-12-24
  • 打赏
  • 举报
回复
好像有点懂了。
Base1 *b1=&d;
Base2 *b2=&d;
Base3 *b3=&d;
这些就是说改变了指针的值了?为什么会改变啊。
谢谢您。
[Quote=引用 28 楼 pengzhixi 的回复:]

Base3 *b33 = reinterpret_cast<Base3*>(&amp;d);都是指针转换成指针,指针的值并没有变,即指针的所指的对象地址没有变,它的地址应该还是d;所以按照继承顺序调用成员函数。

你这里因为Drived并没有覆写f();所以在d里面实际上有三分f();分别是Base1,Base2,Base3的。至于调用哪一份,这个就是查找顺序的问题了。所以没什么好说……
[/Quote]
pstrunner 2010-12-24
  • 打赏
  • 举报
回复
你这里因为Drived并没有覆写f();所以在d里面实际上有三分f();分别是Base1,Base2,Base3的。至于调用哪一份,这个就是查找顺序的问题了。所以没什么好说的。
==============================================
同意。
pengzhixi 2010-12-24
  • 打赏
  • 举报
回复
Base3 *b33 = reinterpret_cast<Base3*>(&d);都是指针转换成指针,指针的值并没有变,即指针的所指的对象地址没有变,它的地址应该还是d;所以按照继承顺序调用成员函数。

你这里因为Drived并没有覆写f();所以在d里面实际上有三分f();分别是Base1,Base2,Base3的。至于调用哪一份,这个就是查找顺序的问题了。所以没什么好说的。
pstrunner 2010-12-24
  • 打赏
  • 举报
回复
Base3 *b33 = reinterpret_cast<Base3*>(&d);都是指针转换成指针,指针的值并没有变,即指针的所指的对象地址没有变,它的地址应该还是d;所以按照继承顺序调用成员函数。

Base3 *b3=&d;是Base3的地址,它类似于以下转换:
Base3 *b4 = dynamic_cast<Base3*>(&d);

还有多重继承情况下,每个基类都会有不同的地址,第一个基类地址与派生类地址是相同的。

[Quote=引用 23 楼 shwarpine 的回复:]
我的意思是为什么b3->f()和b33->f()输出来的东西不一样,
Base3 *b3=&d;

Base3 *b33 = reinterpret_cast<Base3*>(&d);

他们不应该是一样的吗,只不过多了个东东而已,那个东东有什么作用。

引用 22 楼 pengzhixi 的回复:

还是我说的,只是一个查找顺序的问题。所以对于这种看似必然的结果……
[/Quote]
pengzhixi 2010-12-24
  • 打赏
  • 举报
回复
1.去掉了virtual 没什么好说的。指针是什么类型那么就输出相应类型的函数


2.这个问题在于这个转型什么都没做,没有调整b33这个指针的指向,它只是说要将d对象当成Base3对象来看待,但是并不调整b33的指向。对b33->fun();来说,调用任何一个基类的f()都是合理的。只是根据动态绑定的原则实际对象是什么就调用对应的f();但是对b33来说实际对象就是d,而派生类没有重写f();你可以说
d is base1,也可以说d is base2,d is base3。所以,这里应该有一个调用上的三义(因为有3个直接基类)。 只是编译器根据一个继承顺序,选择了第一个而已。所以就有你看到的结果。 实际上这个例子本身就有问题。
shwarpine 2010-12-24
  • 打赏
  • 举报
回复
我输了啊,我看到原因了,b3和b33指针的值都不一样,可是我想不通啊,为什么呢。

Base3 *b3=&d;
Base3 *b33 = reinterpret_cast<Base3*>(&d);

就一个reinterpret_cast<Base3*>不一样,怎么就导致了值不一样了。

现在我把Base1,Base2,Base3里面的virtual关键字都去掉了,
地址的分配还是和上面一样,但是
b33->f();//输出的却变成了Base3::f
可能我太笨吧,说的也有些乱,不懂这是为什么,麻烦您了。
[Quote=引用 24 楼 pengzhixi 的回复:]

Base1 *b1 = &d;//输出:Base1::f
cout<<(void*)b1<<endl;
Base2 *b2 = &d;//输出:Base2::f
cout<<(void*)b2<<endl;
Base3 *b3=&d;//输出:Base3::f
cout<<(void*)b3<<endl;
Base1 *b33 = reinterpret_ca……
[/Quote]
加载更多回复(15)

65,210

社区成员

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

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