【问题标题】:Assigning derived class object to a parent class reference将派生类对象分配给父类引用
【发布时间】:2012-01-13 12:37:07
【问题描述】:

当我看到时我总是很困惑:

Parent ref = new Child();

子类扩展父类的地方。

  1. 对象ref 在内存中的样子如何?
  2. 如何处理虚方法?非虚拟的?
  3. 与以下有何不同:
Child ref = new Child();

【问题讨论】:

  • 我希望不会,我讨厌被骗回答作业。我相信他为什么要问他的话。也许我应该摆脱我的答案......
  • 我想给 OP 怀疑的好处吧?
  • @StuartGolodetz - 我说绝对。如果他在作业上作弊,而我不是说他是,那么对他来说更糟的是期末考试时间。无论如何,这个问题是清晰、智能的,并且为 Stack Overflow 提供了很好的内容。
  • 不是作业。我是一个工作的专业人士。我在面试中被问到这个问题,从那以后我一直很困惑。
  • 尽量不要使用'ref'——它是一个关键字。

标签: c# oop inheritance polymorphism


【解决方案1】:

ref 是一个 Child 对象。在Child 类上调用虚拟方法。但是,仅在 Child 类中定义的方法在分配给 Parent 对象时不可见。

如果foo() 不是虚拟的,那么编译将根据变量ref 的声明类型选择一个方法。如果您有Parent ref = new Child();,则将调用Parent.foo()。如果您有Child ref = new Child();,则将调用Child.foo()。当然,这种情况下C#编译器会要求你在Child.foo()的声明中使用new来表示你的意思是隐藏Parent中的实现。

【讨论】:

    【解决方案2】:

    我想ref 只包含可以找到引用的Child 对象的地址。如果调用虚方法,实际调用的方法取决于对象的动态类型(Child);如果调用非虚拟方法,则取决于静态类型 (Parent)。它与Child ref = ... 不同,因为在那个中,静态类型是Child 而不是Parent

    我希望这不是家庭作业:)

    【讨论】:

      【解决方案3】:

      这样想(假设 Parent 不是抽象类)

      Parent ref = new Child();
      

      Parent ref = new Parent();
      

      大部分是相同的,除了在 Child 中重写的 virtual 方法将在前者中被调用,而不是后者。

      您声明对象的类型将决定对象上可用的方法。将对象声明为比您实例化它的类型更不具体的类型(前一种情况)可以影响在运行时调用哪些方法,但前提是声明了这些方法作为抽象的或虚拟的。

      在任何一种情况下,假设您在 ref 上调用了一个方法 foo。运行时可以在类Parent 上使用方法foo。然后运行时会查看foo 是否是虚拟的(或抽象的)。如果foo 不是虚拟的或抽象的,则运行时将立即调用foo 父级定义,并完成它。但是,如果foo 虚拟的或抽象的,运行时将检查 ref 是否真的实例化为覆盖foo 的更具体的类型。如果是这样,它会调用 that foo

      【讨论】:

      • 如果 foo 在 Parent 和 Child 类中都作为非虚拟方法存在,并且 ref.foo() 被调用。叫哪一个?
      • 会根据ref的类型调用该方法。如果您有Parent ref = new Child();,则将调用Parent.foo()。如果您有Child ref = new Child();,则将调用Child.foo()。我已将此添加到我的答案中。
      • @TingYun - drdwilcox 说得再清楚不过了
      【解决方案4】:

      对象在内存中的外观如何?

      您的问题不清楚。有两个相关的内存位置。 变量与存储位置相关联。该存储位置包含对另一个存储位置的引用

      变量的存储位置通常实现为包含“托管指针”的 4 或 8 字节整数——垃圾收集器已知的内存地址。

      对象的内存布局也是CLR的一个实现细节。与对象关联的内存缓冲区将包含对象的所有数据——字段的所有值等等。它还包含对另一个内存位置的引用,即对象的虚函数表

      然后,虚函数表 (vtable) 包含更多的引用,这一次引用指向与对象的最派生类型相关联的方法。

      虚方法如何处理?非虚拟的?

      通过从变量中查找对象引用,然后查找 vtable,然后在 vtable 中查找方法,然后调用该方法来执行虚拟方法。

      非虚拟方法不会通过 vtable 调用,因为它们在编译时是已知的。

      它与...有何不同

      对象上调用的非虚方法会根据变量的类型调用方法的版本。对象上调用的虚方法会根据变量所引用的对象的类型调用方法的版本。

      如果还不是很清楚,您可能想阅读我的文章,该文章解释了如何在没有虚拟方法的语言中“模拟”虚拟方法。如果您能理解如何用一种没有虚拟方法的语言自己实现虚拟方法,那将有助于您了解我们实际上是如何确实实现虚拟方法的。

      http://blogs.msdn.com/b/ericlippert/archive/2011/03/17/implementing-the-virtual-method-pattern-in-c-part-one.aspx

      【讨论】:

      • 谢谢。这就是我一直在寻找的。迄今为止最好的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-02
      • 2011-04-14
      • 1970-01-01
      • 1970-01-01
      • 2020-03-24
      • 2010-10-18
      • 1970-01-01
      相关资源
      最近更新 更多