当我看到时,我总是感到困惑:
Parent ref = new Child();
其中Child类扩展Parent。
ref
在内存中的样子如何?Child ref = new Child();
答案 0 :(得分:6)
对象在内存中的外观如何?
你的问题不清楚。有两个相关的内存位置。 变量与存储位置相关联。该存储位置包含引用到另一个存储位置。
变量的存储位置通常实现为四字节或八字节整数,其中包含“托管指针” - 垃圾收集器已知的内存地址。
对象的内存布局也是CLR的实现细节。与对象关联的内存缓冲区将包含对象的所有数据 - 字段的所有值和诸如此类的东西。它还包含对另一个内存位置的引用,该对象的虚拟函数表。
虚函数表(vtable)然后包含甚至更多的引用,这次引用引用与对象的派生类型最相关的方法。
如何处理虚拟方法?非虚?
通过从变量中查找对象引用,然后查找vtable,然后在vtable中查找方法,然后调用该方法来执行虚方法。
非虚方法不是通过vtable调用的,因为它们在编译时是已知的。
与...有什么不同?
在对象上调用的非虚方法将根据变量的类型调用方法的版本。调用对象的虚方法将根据变量引用的对象的类型调用方法的版本。
如果不是很清楚,您可能需要阅读我的文章,该文章解释了如何使用没有它们的语言“模拟”虚拟方法。如果您能够理解如何使用没有它们的语言自己实现虚拟方法,那么这将有助于您了解我们实际 实现虚拟方法的方式。
答案 1 :(得分:2)
ref
是Child
个对象。在Child
类上调用虚方法。但是,仅在Child
类中定义的方法在分配给Parent
对象时不可见。
如果foo()
不是虚拟的,那么编译将根据声明的变量ref
类型选择一个方法。如果您有Parent ref = new Child();
,则会调用Parent.foo()
。如果您有Child ref = new Child();
,则会调用Child.foo()
。当然,在这种情况下,C#编译器会要求您在new
的声明中使用Child.foo()
来表示您要隐藏Parent
中的实现。
答案 2 :(得分:0)
我想ref
只包含可以找到引用的Child
对象的地址。如果调用虚方法,则调用的实际方法取决于对象的动态类型(Child
);如果调用非虚方法,则取决于静态类型(Parent
)。它与Child ref = ...
不同,因为在那一个中,静态类型是Child
而不是Parent
。
我希望这不是作业:)。
答案 3 :(得分:0)
以这种方式思考(假设Parent不是抽象类)
Parent ref = new Child();
和
Parent ref = new Parent();
大致相同,除了在Child中覆盖的虚拟方法将被调用,而不是后者。
您声明对象的类型将确定哪些方法可用。声明一个对象是一个不太具体的类型而不是你实例化它 - 前一种情况 - 可以影响在运行时调用哪些方法,但只有在声明了那些方法的情况下作为抽象或虚拟。
在任何一种情况下,假设您在ref上调用了方法foo
。运行时可以在类foo
上使用方法Parent
。然后,运行时将查看foo
是否为虚拟(或抽象)。如果foo
不是虚拟的或抽象的,那么运行时会调用foo
父定义然后在那里,并完成它。但是,如果foo
是虚拟或抽象,则运行时会检查ref 是否真的实例化为覆盖foo
的更具体类型。如果是这样,它会调用 foo