继承的内存分配

hp44 2010-01-22 11:22:27
子类继承父类,在内存中的分配是怎么样的?最开始在堆内存里分配了父类对象的,那么在子类对象中,是不是还包含了一份父类对象的拷贝,或者只有一个父类的base引用呢?
...全文
433 38 打赏 收藏 转发到动态 举报
写回复
用AI写文章
38 条回复
切换为时间正序
请发表友善的回复…
发表回复
hp44 2010-01-24
  • 打赏
  • 举报
回复
真要有10层类的话,父类的每个字段,都应该在子类对象的内存中存在,也应该包括私有的,那不就相当于子类对象里的肚子里有一个父类字段区域么
RFEZGC 2010-01-24
  • 打赏
  • 举报
回复
如果 B从A继承

那么 实力化 B的时候, A也在B中被初始化, 可以从分配的内存地址的长度看看,如果A的长度为2,而B为3,那么B被实力化的时候的长度就为5,

LZ 说的拷贝和引用,俺怎么看怎么都觉得意思是一个样的.
其实是,B在初始化的时候,A也被初始化了.从先后的顺序上看,是A先初始化



因为 C# 是不允许 有多个CLASS 被同时继承的(例如菱形继承),这样也就避免了C++的 多基类问题(当然C++可以用虚基类实现的)

楼上居然有人 把这个问题扯到 多太性上面去,

hp44 2010-01-22
  • 打赏
  • 举报
回复
父类对象一定会被先创建,这有什么问题呢,如果子类对象保存的是父类对象的引用,那么在方法列表中,父类方法指针,也是指向的子类所属的方法列表么?
  • 打赏
  • 举报
回复
.net所实现的继承的规范,是比较符合OO的。因此,我们不管它是这样在底层实现的,至少你在A中定义的代码,它这个程序中运行时就是在一个B类实例上。就好象说你是男人,男人是人,你不能说你这个男人有一个base指针指向了另一个(你这个人)鬼魂,你不能把你跟鬼魂并列起来(否则你有多少种称号就会凭空多出多少个鬼魂附体了)。男人hp44就是人hp44,而人hp44就是男人hp44,两个指称其实都是指着同一个人。

至少,.net给我们实现了真正的OO概念。不管底层如何实现,我们至少应该正常地按照OO的继承概念去理解,而不要纠缠于什么指针。
满衣兄 2010-01-22
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 hp44 的回复:]
这里不需要多态的概念,只要把基本的内存分配搞定就可以了,父类对象肯定存在,子类里包含的是父类的引用指针呢还是父类对象的拷贝
[/Quote]
是父类对象的指针,
创建子类时父类同时被创建,而且首先要创建父类对象,然后再创建子类对象,没有父类哪有子类,就是这个道理。显然,在子类里拷贝一份父类对象不但毫无意义而且也是在浪费资源,所以显然保存的是指针了。
  • 打赏
  • 举报
回复
让你自认为的“肯定存在的父类对象”自己说说自己是谁,这还不够吗?

你的问题就出在以底层为规范的心态上。研究别人的代码固然要看底层的东西,但是研究规范则必须看高层次的东西。编写测试程序让你自认为存在的东西自己说说,它自己到底是什么,这样好不好?
  • 打赏
  • 举报
回复
谁在跟你讨论什么多态了呢?真的很奇怪。
hp44 2010-01-22
  • 打赏
  • 举报
回复
这里不需要多态的概念,只要把基本的内存分配搞定就可以了,父类对象肯定存在,子类里包含的是父类的引用指针呢还是父类对象的拷贝
  • 打赏
  • 举报
回复
有些人可能以为作为B的父类A中的代码就应该认定自己的对象实例是一个A类型?甚至有人说什么B实例里边的base是一个什么A类型的内部对象?

错。
  • 打赏
  • 举报
回复
我给你贴出打印出来的结果:
我的类型是ConsoleApplication1.B,我的HashCode是45653674。
我的类型是ConsoleApplication1.B,我的HashCode是45653674。
  • 打赏
  • 举报
回复
底层如何实现的,能代表高层的概念吗?或许,聪明反被聪明误,底层可以有不同的实现形式,例如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());
}
}
}
ztenv 2010-01-22
  • 打赏
  • 举报
回复
就是完整的父类的实例内存块+派生类所添加的内存块=派生类实例的内存块
wiki14 2010-01-22
  • 打赏
  • 举报
回复
http://www.javaeye.com/problems/6862

虽然是JAVA的,但面向对象的思想是一样一样的。
lovexilove 2010-01-22
  • 打赏
  • 举报
回复
up
流氓兔 2010-01-22
  • 打赏
  • 举报
回复
单步调试一下程序,不就可以了吗?
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是个好东东!

cuike519 2010-01-22
  • 打赏
  • 举报
回复
可以理解为类是模板,对象是通过这个模板构造出来的实例,实例将自己保存在内存里面。
清风六月 2010-01-22
  • 打赏
  • 举报
回复
LZ去理解下继承的意义啊!
hp44 2010-01-22
  • 打赏
  • 举报
回复
父类对象空间,和子类字段空间,共同组成了子类对象空间
jin20000 2010-01-22
  • 打赏
  • 举报
回复
这是一段

★这种初始化顺序是由内存分配机制决定的,如我们调用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.
jin20000 2010-01-22
  • 打赏
  • 举报
回复
我觉得父类和子类的内存分配肯定没什么联系,而是说一个类在分配内存时时怎么样分配的,包括变量,方法呀,静态的,虚方法等等
加载更多回复(17)

111,086

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

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