免费注册 查看新帖 |

Chinaunix

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

[C++] 基类指针变量都被赋值派生类的地址,但为什么不相等呢? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-10-06 13:39 |只看该作者 |正序浏览
#include <stdio.h>
#include <iostream>
using namespace std;


class A
{
public:
        int n;
        virtual  void fun()
        {
                cout<<"A"<<endl;
        }
};


class B
{
public:
        int n;
        virtual  void fun()
        {
                cout<<"B"<<endl;
        }
};

class C : public A, public B
{
public:
        int n;
        virtual  void fun()
        {
                cout<<"C"<<endl;
        }
};


void main()
{
        C    c;
        A*   pa = &c;
        B*   pb = &c;
        //A*   pa = static_cast<B*>(&b);

        c.fun();
        pa->fun();
        pb->fun();

        cout<<"&c = "<<&c<<endl;
        cout<<"pa = "<<pa<<endl;
        cout<<"pb = "<<pb<<endl;

        return;

}


输出:

C
C
C
&c = 0012FF6C
pa = 0012FF6C
pb = 0012FF74


为什么 &c 和 pa 相等,pb 不等?

论坛徽章:
0
14 [报告]
发表于 2012-10-09 10:36 |只看该作者
本帖最后由 twinsen724 于 2012-10-09 14:35 编辑

问题在于这就是C++的MI模型,这个继承体系的模型可以看作(在内存中):

A subobject => &c & pa
B subobject => pb
C subobject

对于MI来说,A和B的sub object的起始地址不一样,这不同于SI(SI使用重叠模型)。但C因为继承于A和B,而A是第一个base class,B是第二个base class,在MI中,derived class subobject的起始位置和第一个base class subobject重合,从第二个base class subobject以及之后的,都有自己的独立起始地址。因此

&c == pa == &class A subobject
pb == &class B subobject

根据上面的描述,在MI中,用第二个以及之后的base class pointer保存一个derived class object的地址,compiler需要调整this指针,就在这一步:

B* pb = &c;

this调整到了B subobject的位置,因此造成了你所看到的现象。另外,因为你的hierarchy中有virtual function,这更佳复杂化了MI的内部操作。因为vptr的缘故:

pb->func();

也需要调整this指针,从而指向derived class subobject的地址,实际上就是C object的起始地址&c。

MI是传统C++复杂度的罪魁祸首,如果你再给你的hierarchy加上virtual base class,你会发现自己进入了迷宫。

论坛徽章:
0
13 [报告]
发表于 2012-10-08 20:56 |只看该作者
多重继承的关系?
我不清楚标准里面关于多重继承是怎么规定的,但看样子很像是因为多重继承的关系。
在多重继承的情况下,C在内存中的布局可能是:
_vfptr for A;
A::n;
_vfptr for B;
B::n;
C::n;
所以pa指向的是__vfptr for A, 而pb指向的是 __vfptr for B, 这两个之间还有一个int A::n, 所以差了8个字节。

我对c++的对象模型和多重继承都不清楚,所以只是猜测。

论坛徽章:
6
技术图书徽章
日期:2013-11-13 11:11:27子鼠
日期:2014-02-20 17:54:13处女座
日期:2014-06-16 17:43:33午马
日期:2014-08-08 09:11:17未羊
日期:2014-08-10 11:57:072015年辞旧岁徽章
日期:2015-03-03 16:54:15
12 [报告]
发表于 2012-10-08 15:22 |只看该作者
本帖最后由 littledick 于 2012-10-08 15:26 编辑

指针指向的实例都是C类型的啊。{:3_198:}

用基类容器管理多类型子类实例集合时,通常会给子类实例在构造时就标识类型。在使用基类的指针作为容器操作参数,避免最后使用时出错。

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
11 [报告]
发表于 2012-10-08 12:57 |只看该作者
i don't know any more, let me go.

论坛徽章:
0
10 [报告]
发表于 2012-10-08 12:28 |只看该作者
linux_c_py_php 发表于 2012-10-08 12:21
就是A类和B类一模一样, 第二个转换其实是将C对象中A对象的地址赋值给B的指针, 对于B指针来说, 因为A对象和B ...


即使A和B的内存布局不一样(如下面),也是同样的啊。

class A
{
public:
        int n;
        int m;
        int k;
        virtual  void fun()
        {
                cout<<"A"<<endl;
        }
};


class B
{
public:
        char  c;
        virtual  void fun()
        {
                cout<<"B"<<endl;
        }
};

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
9 [报告]
发表于 2012-10-08 12:21 |只看该作者
就是A类和B类一模一样, 第二个转换其实是将C对象中A对象的地址赋值给B的指针, 对于B指针来说, 因为A对象和B对象的内存布局是一样的, 所以调用B->xxx和调用A->xxx是一样的.

cppfans6 发表于 2012-10-08 12:16
不明白,你这个回答好像不是我要问的,请详解。

论坛徽章:
0
8 [报告]
发表于 2012-10-08 12:16 |只看该作者
linux_c_py_php 发表于 2012-10-08 12:12
前者指向B基类, 后者指向A基类, 最后都是调C派生类, 而且A, B长的一样, 不费解吧.


不明白,你这个回答好像不是我要问的,请详解。

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
7 [报告]
发表于 2012-10-08 12:12 |只看该作者
本帖最后由 linux_c_py_php 于 2012-10-08 12:12 编辑

前者指向B基类, 后者指向A基类, 最后都是调C派生类, 而且A, B长的一样, 不费解吧.

论坛徽章:
0
6 [报告]
发表于 2012-10-08 12:03 |只看该作者
linux_c_py_php 发表于 2012-10-08 11:58
B是C的基类, 虚函数不调用C的调用谁的?


这个我知道,我问的是

B*   pb = &c;  和  B*   pb = reinterpret_cast<B*>(&c);

两个pb数值不同,但还都能保证pb->fun(); 调用的是C的fun,编译器怎么处理的?
  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP