免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2226 | 回复: 7
打印 上一主题 下一主题

[C++] C++对象内存分布问题求教 [复制链接]

论坛徽章:
1
狮子座
日期:2013-09-29 16:47:13
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-04-10 21:13 |只看该作者 |倒序浏览
本帖最后由 silverzhaojr 于 2013-04-10 21:15 编辑

大家好,前几天我在应聘时遇到了一个笔试题目,如下:

  1. class A
  2. {
  3. public:
  4.     A()
  5.     {
  6.         clear();
  7.     }

  8.     void clear()
  9.     {
  10.         memset(this, 0, sizeof(*this));
  11.     }

  12.     virtual void func()
  13.     {
  14.         cout << "func" << endl;
  15.     }
  16. };

  17. class B : public A
  18. {

  19. };

  20. int main()
  21. {
  22.     A oa, *pa, *pab;
  23.     B ob, *pb;

  24.     pa = &oa;
  25.     pab = &ob;
  26.     pb = &ob;

  27.     oa.func(); //----------(1)
  28.     pa->func(); //----------(2)
  29.     ob.func(); //----------(3)
  30.     pb->func(); //----------(4)
  31.     pab->func(); //----------(5)
  32. }
复制代码
题目问的是程序的执行结果。

感觉应该和 C++ 的对象模型有关,我也不是很清楚,当时就随便答了一下。

回来后测试了一下,发现结果是:

  1. func
  2. 执行出错!
  3. func
  4. func
  5. func
复制代码
有谁能解释一下为什么吗?谢谢了!

论坛徽章:
1
技术图书徽章
日期:2014-03-06 15:32:30
2 [报告]
发表于 2013-04-10 22:18 |只看该作者
一般情况,虚函数表指针是抽象类对象的第一个元素。memset将这个指针清0了,再通过虚函数表访问就失效。
class A 大概布局像这样:
struct A;
struct A_methods { void (* func)(A *This), };
struct A {
  A_methods *vtable;
};

void _A_func(A *This) { printf(...); }//等价于: virtual void A::func() { printf(...); }

void _A_clear(A *This) {  //等价于 void A::clear() { memset(this, 0, sizeof(*this); }
    memset(This, 0, sizeof(*This));
}
static
A_methods vtable_A { &_A_func, };
//构造函数像这样
void _A_construct ( A *This ) {
          This->vtable = vtable_A;//有虚函数的,构造时都会隐含执行这个操作
         _A_clear(This); //刚设置好虚函数表,然后又清空了;
}
A a;
A *pa = &a;
//pa->func() 是这样执行的:
pa->vtable->func(pa);//为什么崩溃看清了吧?vtable已经在构造函数里清0了,再用这个指针...
//

论坛徽章:
1
狮子座
日期:2013-09-29 16:47:13
3 [报告]
发表于 2013-04-11 10:02 |只看该作者
回复 2# selfrun


    多谢楼上的,解释的很清楚!

论坛徽章:
0
4 [报告]
发表于 2013-04-11 11:19 |只看该作者
:wink::wink::wink::wink::wink::wink::wink::wink::wink:

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
5 [报告]
发表于 2013-04-11 14:18 |只看该作者
有几点要考虑:
1. sizeof(A) 和 sizeof(B) 都是4;这4个字节都是vptr占的
2. 非多态调用和多态调用成员函数,编译器转化的代码,非多态调用相当于调函数把this做参数传给函数处理
    多态调用函数则是通过vptr来处理。这个过程在《深入探索C++对象模型》中12、13页有个例子相似,其
    他章节楼主也可以参考
3. 为什么 pab->func();  调用不会挂掉?
    1中的A、B的sizeof是一样的,也就是说B的对象应该是不含A的vptr的,只含B自己的vptr,所以在B中的
    A部分大小应该是0,因为没有data member,我在clear()中加打印sizeof(*this),B构造时打印的是4,
    可能是因为this是B*,但是A作为base类先构造,虽然把4字节清0,但是之后B开始构造还是会初始化出
    正确的vptr,所以pab->func()也不会挂掉。

论坛徽章:
0
6 [报告]
发表于 2013-04-11 16:33 |只看该作者
回复 5# cokeboL


    如此说来,基类中对vptr的赋值是多余的?是不是在子类实例构造中的基类构造时,根本没有对vptr赋值,而是在自身的构造函数内赋值的?

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
7 [报告]
发表于 2013-04-11 16:52 |只看该作者
回复 6# Frahm


vptr应该是只存储本类实现或者继承来的那些virtual函数,因为从调用的角度讲,多态的调用方式

base 指针->func(),按照base指针类型找函数可能是错的,所以要从virtual table中找相应函数,

那么,virtual table中,因为多态方式下,调用的是这个类要用的那个函数,所以只需要保存本类用

的那套virtual函数,所以只有一个vptr一个virtual table就ok了,按理说derived类中的base类构造

时不需要初始化vptr,因为derived类中没有这个东西,但是编译器具体实现有可能按照地址进行了

构造,现实如何我就不知道了。

另外多继承的情况的sizeof,我之前看过,时间久了,现在忘记了,有点蒙,在重新看《深入探索c++
对象模型》

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
8 [报告]
发表于 2013-04-12 14:27 |只看该作者
回复 6# Frahm


找到了,《深入探索c++对象模型》 32 页倒数第二段说得很明白:

    编译器必须确保如果某个 object 含有一个或以上的 vptrs,那些vptrs的内容不会被 base class object 初始化或改变。

——再参考44-45页内容,vptrs 在编译期间产生,内含相关的 class vtbl 的地址。至于 vptrs 的值,目测应该是在对象初始化阶段,并且
     在 base class 构造之后
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP