谈谈今天的面试题:关于C++继承的编译器实现

MagiSu 2013-07-23 07:28:49
今天面试了一个硬件公司,软件部分主要是做驱动和辅助软件的。面试官问的题目很简单:如果你设计C++语言的继承,你会怎么办。

首先是考虑单继承问题。我们都知道单继承时每个对象都带一个vptr,指向相应的vtable。那么形如

class A
{
public:
virtual void dosth() { dosth1... }
int dataA;
};
class B : public A
{
public:
virtual void dosth() { dosth2... }
int dataB;
};

的继承结构,如果有

B* ptr=new B;

void func(A* p)
{
p->dosth();
}

func(ptr);

这个结构,那么func接受到的指针是类型A的,如何知道这个object其实是B类型的?

我的答案是通过访问对象的虚表,即可得到对象的实际类型。

2. 现在考虑多重继承:

class C
{
public:
virtual void dosth1() {...}
int data;
};

class D: public A, public C
{
public:
virtual void dosth();
virtual void dosth1();
int dataD;
};

那么:

D* p1=new D;

void func1(C* p)
{
p->dosth1();
}

func1(p1);

这里func1得到的是p1指向的对象中C部分的指针,如何让函数知道它其实是一个D对象?

我的答案是在C对象处加上对应的vptr。

现在的问题就是:假设D中重载的dosth1()需要使用D的dataD,如何让func1调用dosth1()时得到正确的dataD的地址?

我的答案是在vtable中加入对不同基类的对应的偏移。他说还有更好的方法,不要访问vtable。我说在每个对象的基类部分中加入this指针的位置,他说这样浪费大量内存。

现在我就是想问问,有什么不访问vtable又能对对对象类型进行识别的方法?不使用RTTI的话怎么处理呢?
...全文
880 34 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
34 条回复
切换为时间正序
请发表友善的回复…
发表回复
mujiok2003 2013-08-01
  • 打赏
  • 举报
回复
更好的办法ATL中的接囗查询技术
zybjtu 2013-08-01
  • 打赏
  • 举报
回复
引用 楼主 MagiSu 的回复:
今天面试了一个硬件公司,软件部分主要是做驱动和辅助软件的。面试官问的题目很简单:如果你设计C++语言的继承,你会怎么办。 首先是考虑单继承问题。我们都知道单继承时每个对象都带一个vptr,指向相应的vtable。那么形如 class A { public: virtual void dosth() { dosth1... } int dataA; }; class B : public A { public: virtual void dosth() { dosth2... } int dataB; }; 的继承结构,如果有 B* ptr=new B; void func(A* p) { p->dosth(); } func(ptr); 这个结构,那么func接受到的指针是类型A的,如何知道这个object其实是B类型的? 我的答案是通过访问对象的虚表,即可得到对象的实际类型。 2. 现在考虑多重继承: class C { public: virtual void dosth1() {...} int data; }; class D: public A, public C { public: virtual void dosth(); virtual void dosth1(); int dataD; }; 那么: D* p1=new D; void func1(C* p) { p->dosth1(); } func1(p1); 这里func1得到的是p1指向的对象中C部分的指针,如何让函数知道它其实是一个D对象? 我的答案是在C对象处加上对应的vptr。 现在的问题就是:假设D中重载的dosth1()需要使用D的dataD,如何让func1调用dosth1()时得到正确的dataD的地址? 我的答案是在vtable中加入对不同基类的对应的偏移。他说还有更好的方法,不要访问vtable。我说在每个对象的基类部分中加入this指针的位置,他说这样浪费大量内存。 现在我就是想问问,有什么不访问vtable又能对对对象类型进行识别的方法?不使用RTTI的话怎么处理呢?
看看MFC的动态类型识别技术就好了。 其根本方法是利用宏,在每个类里面插入一个节点,这个节点会像链表一样,指向它的前驱。进而形成一张目录网
mujiok2003 2013-08-01
  • 打赏
  • 举报
回复
dynamic_cast
mujiok2003 2013-08-01
  • 打赏
  • 举报
回复
rtti(运行时类型标识)
cocoabird 2013-08-01
  • 打赏
  • 举报
回复
可能是看你理解多深,估计他也没别的方法
Saleayas 2013-08-01
  • 打赏
  • 举报
回复
不要理这种公司。
bjyustb 2013-08-01
  • 打赏
  • 举报
回复
弱弱的问一句为啥不用typeid或者dynamic_cast? 考题主旨是什么?考的是底层内存布局以及虚表的访问还是什么?菜鸟表示没看懂。。。
yrnaaa 2013-07-31
  • 打赏
  • 举报
回复
引用 26 楼 wangdahu888 的回复:
[quote=引用 24 楼 yrnaaa 的回复:] [quote=引用 21 楼 wangdahu888 的回复:] 看下MFC的中怎么实现RTTI,不就行了
现在不都用net了?还用mfc?[/quote] 大神此话从何说起[/quote] 不懂,纯粹提问,我买了本mfc放了好几年也没看一页。。。
  • 打赏
  • 举报
回复
引用 24 楼 yrnaaa 的回复:
[quote=引用 21 楼 wangdahu888 的回复:] 看下MFC的中怎么实现RTTI,不就行了
现在不都用net了?还用mfc?[/quote] 大神此话从何说起
www_adintr_com 2013-07-23
  • 打赏
  • 举报
回复
VC 的做法是, C 的虚函数表中, 不是直接放 D::dosth1 函数的地址, 而是一段 thunk 代码的地址. 这段代码基本上是这样的: sub ecx, xxxx jmp D::dosth1
ForestDB 2013-07-23
  • 打赏
  • 举报
回复
第一,我会摒弃多继承; 第二,我会摒弃vtable实现。
yrnaaa 2013-07-23
  • 打赏
  • 举报
回复
引用 21 楼 wangdahu888 的回复:
看下MFC的中怎么实现RTTI,不就行了
现在不都用net了?还用mfc?
healer_kx 2013-07-23
  • 打赏
  • 举报
回复
嗯,我的英文也不行,在两个外企干过了。。。凑合事吧。。。 继承是否有虚表,也得看是否存在虚函数啊。单纯的继承不需要虚表。 这是最简单的情况了,编译器只要考虑不同的对象调用不同函数,static invoke到哪个函数就行了。
MagiSu 2013-07-23
  • 打赏
  • 举报
回复
引用 9 楼 gogdizzy 的回复:
晕,看了半天没看懂楼主的问题。 是说让你重新设计继承类的内存布局吗? 如果按照当前编译器的“标准做法”,就是访问虚表啊,不访问虚表,也需要有其它分支判断,才能决定是执行C还是D类的方法。 面试官到底什么意思?提供和访问虚表等价的另一种方法?
关键是电话面试,又是纯英文面,按说我平时和洋人交流也没啥大问题啊,为啥真谈起来就有点头疼呢?
  • 打赏
  • 举报
回复
晕,看了半天没看懂楼主的问题。 是说让你重新设计继承类的内存布局吗? 如果按照当前编译器的“标准做法”,就是访问虚表啊,不访问虚表,也需要有其它分支判断,才能决定是执行C还是D类的方法。 面试官到底什么意思?提供和访问虚表等价的另一种方法?
Defonds 2013-07-23
  • 打赏
  • 举报
回复
继承和封装,考察的都是基本功
橡木疙瘩 2013-07-23
  • 打赏
  • 举报
回复
而在使用D*指针调用dosh1时,先将D*转换为C*再去调用。

D obj;
C * pc = & obj;
D * pd = & obj;
pc->dosth1();//(pc->vtable[DOSTH1])(pc);
pd->dosth1();//(static_cast<C*>(pd)->vtable[DOSTH1])(static_cast<C*>(pd));


void D::dosth1() //void D_dosth1( C * param_this );
{
    //this = static_cast<D*>(param_this);
    dataD ++;//this->dataD ++;
}
jiandingzhe 2013-07-23
  • 打赏
  • 举报
回复
我觉得题目有些不清楚:上述的各种“实现”,是指编译的时候,编译器如何实现继承的语义,将其具现成汇编代码;还是运行时的RTTI?
橡木疙瘩 2013-07-23
  • 打赏
  • 举报
回复
引用 4 楼 MagiSu 的回复:
这个时候func1如何处理带进的指针pc?已知pc是一个类型为C*的变量,func1不可能在编译时期知道pc指向对象的具体内容。Inside C++ Obj Model提到的处理方法是在虚表里保存对应的偏移量,但是这样就需要在虚表里面保存数据,不符合他不访问续表的要求。
func1不知道pc是一个D,但D::dosth1知道。 D::dosth1应该把this当作一个C*处理,在访问D的数据成员时,从该数据成员在D中的偏移量中减去基类C在D中的偏移量(这一步同样可以在编译器完成),然后再应用偏移量访问数据。 而在使用D*指针调用dosh1时,先将D*转换为C*再去访问。
MagiSu 2013-07-23
  • 打赏
  • 举报
回复
还有一种可能的处理办法,自动为不同的输入指针类型提供不同的函数,类似模板的偏特化,但是模板偏特化不能用在函数上啊。 template <T> void func1<C>(C* pc) { C::dosth1(pc); } template <> void func1<D>(D* pd) { D::func1(pd-offset_of_c_and_d); }
加载更多回复(13)

65,184

社区成员

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

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