子类继承父类,在内存中的分配是怎么样的?最开始在堆内存里分配了父类对象的,那么在子类对象中,是不是还包含了一份父类对象的拷贝,或者只有一个父类的base引用呢?

解决方案 »

  1.   

    http://www.javaeye.com/problems/6862虽然是JAVA的,但面向对象的思想是一样一样的。
      

  2.   

    底层如何实现的,能代表高层的概念吗?或许,聪明反被聪明误,底层可以有不同的实现形式,例如C#跟传统C++对继承的实现完全不同,不能代表什么。关键是要知道规范中对高层是怎么定义的,而一旦遇到不符合高层规范的底层实现,我们反而应该避免使用它。如果不纠缠底层如何实现的,那么我给你写一个基本的测试,你自己分析规范的概念:using System;namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                new B().我到底是谁呢();
                Console.ReadKey();
            }
        }    class A
        {
            public  virtual void 我到底是谁呢()
            {
                Console.WriteLine("我的类型是{0},我的HashCode是{1}。", this.GetType().FullName, this.GetHashCode());
            }
        }    class B: A
        {
            public override void 我到底是谁呢()
            {
                base.我到底是谁呢();
                Console.WriteLine("我的类型是{0},我的HashCode是{1}。", this.GetType().FullName, this.GetHashCode());
            }
        }
    }
      

  3.   

    我给你贴出打印出来的结果:我的类型是ConsoleApplication1.B,我的HashCode是45653674。
    我的类型是ConsoleApplication1.B,我的HashCode是45653674。
      

  4.   

    有些人可能以为作为B的父类A中的代码就应该认定自己的对象实例是一个A类型?甚至有人说什么B实例里边的base是一个什么A类型的内部对象?错。
      

  5.   

    .net所实现的继承的规范,是比较符合OO的。因此,我们不管它是这样在底层实现的,至少你在A中定义的代码,它这个程序中运行时就是在一个B类实例上。就好象说你是男人,男人是人,你不能说你这个男人有一个base指针指向了另一个(你这个人)鬼魂,你不能把你跟鬼魂并列起来(否则你有多少种称号就会凭空多出多少个鬼魂附体了)。男人hp44就是人hp44,而人hp44就是男人hp44,两个指称其实都是指着同一个人。至少,.net给我们实现了真正的OO概念。不管底层如何实现,我们至少应该正常地按照OO的继承概念去理解,而不要纠缠于什么指针。
      

  6.   

    子类对象中并不存在什么父类的对象,父类的东西已经跟子类的融合在一起了。
    base关键字也不是对父类对象的引用,它只是用于从派生类中访问基类的成员。
      

  7.   

    继承和内存布局貌似没有什么直接关系吧?sp1234说的是OO范畴的,想看具体的内存布局挂上调试器看。下面就你的问题回答:Q:子类继承父类,在内存中的分配是怎么样的? 
    A:挂上调试器和SOS可以看到具体的分配情况。对象在托管堆上分配的时候记录一个方法表指针,这个指针描述这个对象的类型。
    每个对象都有他自己独立的存储空间,从内存上来看两者没有什么不同。Q:最开始在堆内存里分配了父类对象的,那么在子类对象中,是不是还包含了一份父类对象的拷贝,或者只有一个父类的base引用呢? 
    A:实现上没有这个指针,前面说了,这两个对象在内存中是相互独立的。
    实现OO的问题不在于内存中如何放,而是CLR如何识别对象,这里元数据是当之无愧的MVP!至于OO的概念,sp1234大仙都说了,俺不废话了
      

  8.   

    既然是独立的对象,又不存在指针,你们子类的this是怎么找继承下来的父类公有变量的,和base是同一概念么
      

  9.   

    对象在内存并不是连续的,包含不同的几个部分,主要的有MethodTable的地址指针、对象实例的数据。
      MethodTable的地址指针是类方法在内存中的描述(这里包含类的方法和方法的实际地址等),同一个类的所有实例的方法都是同一个地址,在方法执行时,对象实例本身是作为参数传入方法内的。对于类继承,子类对象的MethodTable、父类对象的MethodTable关系也不是简单的包含,abstract方法应该是覆盖,其他方法包含base.方法和this.方法等等
      
      

  10.   

    牛人那么多,我就冒个泡,多句嘴 
    Object类里面的getype方法有如下特性
    [MethodImplAttribute(MethodImplOptions.InternalCall)]
        public extern Type GetType(); 
    MethodImplOptions.InternalCall,据说这玩意是CLR实现的方法,偶个人觉得,会"创建"一个父类对象就像是你要创建一个男人,首先应该要先建立一个没有性别特征的人,然后再把缺的东西加上(这说法很黄很暴力)可以这么说吧,当你创建出一个男人之后,你就只有一个男人,而不是一个人外加一个男人(很邪恶的说法)但是,男人也是人.人能做的事情,男人也能做,男人能做的事情,人不一定能做(邪恶~~~)你不应该把父类和子类分割开,认为他们是独立的,实际上他们是一个整体,包含的关系如果你觉得我说的太邪恶,请直听SP1234说的话
      

  11.   

    不管内存连不连续,子类继承父类,先有父类,无论是继承的字段和方法表,都是从根,即父类的成员往下排列。比如A继承B。B的父类是OBJECT。那字段和方法表都是从OBJECT---》B----》A往下排列的。因为类的创建是先父类后子类的递归创建
      

  12.   

    class a
    {
    int dataA;
    void virtual funA(){}
    }class b
    {
    int dataB;
    override void funA(){}
    void funB(){}
    }类a的MethodTable大概是:funA、参数、方法地址。。
    类b的MethodTable大概是:base.funA、参数、方法地址。。
                              this.funA、参数、方法地址。。
                              funB、参数、方法地址。。类a的对象实例大概是:类a的MethodTable的地址、dataA
    类b的对象实例大概是:类b的MethodTable的地址、dataA、dataB
    只是示意、clr的实现会更全面
      

  13.   

    这是一段
    ★这种初始化顺序是由内存分配机制决定的,如我们调用MyClass myClass = new MyClass(), 其产生对象如下:
    1. 计算该类中定义的所有实例字段的size和两个附加对象(type handler(类对象的指针)和syncBlockIndex(指向一块用来管理对象同步的内存))的size并一直递归到object对象,得到其需要分配内存大小, 看剩余内存是否够分配此对象, 不够会导致垃圾回收.
    2. 首先构造MyClass的type对象,type对象包括静态字段和方法表,将其分配在托管堆的loader堆上,注意此对象将不会被GC自动回收, 其生命周期是从创建到AppDomain卸载.
    3. 构造MyClass的实例字段,构造附加type handler并指向type对象, 构造SyncBlockIndex并指向同步内存. 
    4. 调用MyClass构造函数,此时会引起其父类初始化, 父类初始化循环上述过程,直至object对象完成创建, 再返回执行子类构造函数直至MyClass, 创建MyClass完成后, 返回其内存地址, 赋值给MyClass的this,并将其引用传给栈上声明的myClass.
      

  14.   

    单步调试一下程序,不就可以了吗?
    new B().我到底是谁呢();
    new B();先产生一个子类对象(怎么会有父类的对象呢?要是有的话,如果B为10层继承,那需要产生多少对象啊?MS有那么SB吗?)
    Base只是一个关键字,CSC.EXE在编译C#代码时,把base.我到底是谁呢()直接编译为(当然不会是中文的了,应该是2进制代码)
      IL_0002:  call       instance void CLR_Via_CSharp.A::我到底是谁呢()
      IL_0007:  nop
    看见了吗?
    base 关键字用于从派生类中访问基类的成员:
    1>  调用基类上已被其他方法重写的方法。
    2>  指定创建派生类实例时应调用的基类构造函数。
    基类访问只能在构造函数、实例方法或实例属性访问器中进行。
    从静态方法中使用 base 关键字是错误的。
    MSDN是个好东东!
      

  15.   

    如果 B从A继承 那么 实力化 B的时候, A也在B中被初始化,  可以从分配的内存地址的长度看看,如果A的长度为2,而B为3,那么B被实力化的时候的长度就为5,LZ 说的拷贝和引用,俺怎么看怎么都觉得意思是一个样的.
    其实是,B在初始化的时候,A也被初始化了.从先后的顺序上看,是A先初始化因为 C# 是不允许 有多个CLASS 被同时继承的(例如菱形继承),这样也就避免了C++的 多基类问题(当然C++可以用虚基类实现的)楼上居然有人 把这个问题扯到 多太性上面去,