基类的实例如何保存派生类的实例?
我担任 .Net 程序员(不能说我是程序员)已有 2 年了。有一个问题我多年来一直无法理解,那就是基类的实例如何保存派生类的实例?
假设我们有两个类:
class BaseClass
{
public A propertyA;
public B propertyB;
}
class DerivedClass :BaseClass
{
public C propertyC;
}
怎么会发生这种情况:
BaseClass obj = new DerivedClass ()
我的意思是,BaseClass
的内存模型已经没有空间容纳新添加的 propertyC,那么它怎么还能保存 propertyC 的值呢?
另一方面,这怎么可能不会发生:
DerivedClass obj = new BaseClass()
我认为这是正确的方法,因为 DerivedClass
的内存模型具有 BaseClass 的所有空间,甚至更多。但事实并非如此,为什么呢?
我知道我问了一个非常愚蠢的问题,但是有人能给我更详细的答案吗?从内存或编译器的角度来看会更好。
I have been a .Net coder (can not say I am a programmer) for 2 years. There is one question that I can not understand for years, that is how could an instance of the base class hold an instance of the derived class?
Suppose we have two classes:
class BaseClass
{
public A propertyA;
public B propertyB;
}
class DerivedClass :BaseClass
{
public C propertyC;
}
How could this happen:
BaseClass obj = new DerivedClass ()
I mean, the memory model of BaseClass
, has no space for the newly added propertyC, so how could it still hold the value of propertyC?
On the other side, how could this can not happen:
DerivedClass obj = new BaseClass()
I thought this is the correct way since the memory model of DerivedClass
has all the spaces for the BaseClass and even more. But this is not true, why?
I know I am asking a really stupid question, but could someone give me a more detail answer of this? It would be better from the perspective of the memory or compiler.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
因为(这个概念经常被误解)保存指针的变量的类型独立于它所指向的对象的实际具体类型。
因此,当您编写
BaseClass obj
的部分时,会在堆栈上创建一个新的引用变量,编译器会理解该引用以保存对来自 < 或派生自 < 的内容的引用。代码>基类。读取
= new DerivedClass ()
的部分实际上创建了一个DerivedClass
类型的新对象,将其存储在堆上,并存储了一个 指向obj
指向的内存位置中的该对象的指针。堆上的实际对象是DerivedClass
;这被称为对象的具体类型。变量
obj
被声明为BaseClass
类型。这不是一个具体类型,它只是一个变量类型,它仅限制编译器将地址存储到指向的变量中不是BaseClass
或不是从BaseClass
类型派生的对象。这样做是为了保证无论您放入 obj 变量中的是什么,无论它是
BaseClass
还是DerivedClass
的具体类型,它都将具有所有方法、属性和BaseClass
类型的其他成员。它告诉编译器,obj
变量的所有使用都应限制为仅使用BaseClass
类的成员。Because (and this concept is often misunderstood) the variable that holds the pointer is typed independantly from the actual concrete type of the object it points to.
So when you write
the part that says
BaseClass obj
creates a new reference variable on the Stack that the compiler understands to hold a reference to something that is, or derives, fromBaseClass
.The part that reads
= new DerivedClass ()
actually creates a new object of typeDerivedClass
, stores it on the Heap, and stores a pointer to that object in the memory location thatobj
points to. The actual object on the Heap is aDerivedClass
; this is referred to as the concrete type of the object.The variable
obj
is declared to be of typeBaseClass
. This is not a concrete type, it is just a variable type, which only restricts the compiler from storing an address into the variable that points to an object which is not aBaseClass
or which does not derive from the typeBaseClass
.This is done to guarantee that whatever you put into the obj variable, no matter whether it is a concrete type of
BaseClass
orDerivedClass
, it will have all the methods, properties and other members of the typeBaseClass
. It tells the compiler that all uses of theobj
variable should be restricted to uses of only those members of the classBaseClass
.由于
BaseClass
是引用类型,因此obj
不是BaseClass
对象。它是对BaseClass
对象的引用。具体来说,它是对DerivedClass
的BaseClass
部分的引用。Since
BaseClass
is a reference type,obj
is not aBaseClass
object. It is a reference to aBaseClass
object. Specifically, it is a reference to theBaseClass
part of theDerivedClass
.当您这样说时:
您并不是在说“创建一个容器来包含此 BaseClass 对象,然后将这个更大的 DerivedClass 对象塞入其中”。
实际上,您正在创建一个 DerivedClass 对象,并且它是在足以容纳 DerivedClass 对象的内存空间中创建的。 .NET 框架永远不会忘记这个东西是一个 DerivedClass 的事实,也永远不会停止这样对待它。
但是,当您说选择使用 BaseClass 对象变量时,您只是创建一个指向已创建、分配和定义的对象的引用/指针,并且您的指针有点模糊。
就好像房间另一边的那个人是一个红头发的爱尔兰人,体重稍超重,牙齿不好,性格迷人,名叫吉米,但你只是把他称为“那边的那个家伙”。尽管您模糊的描述完全准确,但您对他的描述含糊不清这一事实并不会改变他的身份或他的任何细节。
When you say this:
You are NOT saying "Create a container to contain this BaseClass object, and then cram this bigger DerivedClass object into it".
You are in fact creating a DerivedClass object, and it is being created in a memory space sufficient for a DerivedClass object. Never does the .NET framework lose track of the fact this this thing is specifically a DerivedClass, nor does it ever stop treating it as such.
However, when you say choose to use a BaseClass object variable, you are just creating a reference/pointer to that object that has already been created and allocated and defined and all that, and your pointer is just a little bit more vague.
It's like that guy over there on the other side of the room is a red-headed Irish slightly-overweight chicken-farmer with bad teeth and charming personality named Jimmy, but you are just referring to him as "that dude over there". The fact that you are being vague in describing him does not change what he is or any of his details, even though your vague description is entirely accurate.
答案逐字复制了另一位用户 jk 在 Stackoverflow 上针对完全相同的问题所写的内容。
Answer copied verbatim from what another user, jk, wrote on a exactly same question here on Stackoverflow.
这是可能的,因为内存为基类和派生类分配的方式。在派生类中,首先分配基类的实例变量,然后分配派生类的实例变量。当将基类引用变量分配给派生类对象时,它会看到它期望的基类实例变量以及“额外”的派生类实例变量。
This is possible because of the way memory is allocated for base and derived classes. In a derived class, the instance variables of the base class are allocated first, followed by the instance variables of the derived class. When a base class reference variable is assigned to a derived class object, it sees the base class instance variables that it expects plus the "extra" derived class instance variables.