繁体   English   中英

c ++多态((X *)y) - > foo()vs((X)* y).foo()

[英]c++ polymorphism ((X*)y)->foo() vs ((X)*y).foo()

假设Y是来自类X的派生类,X将foo声明为虚拟。 假设y是(Y *)类型。 然后((X *)y) - > foo()将执行Y版本的foo(),但是((X)* y).foo()将执行X版本。 你能告诉我为什么多态性不适用于解引用的案例吗? 我希望任何一种语法都会产生Y版本的foo()。

您正在切割Y对象部件并将对象复制到X对象中。 然后调用的函数在X对象上调用,因此调用X的函数。

当您在声明或强制转换中指定C ++中的类型时,这意味着声明或转换为对象实际上是该类型的对象,而不是派生类型。

如果你只想处理对象是X类型(也就是说,如果你想要表达式的静态类型是X ,但仍希望它表示Y对象)那么你转换为引用类型

((X&)*y).foo()

这将调用Y对象中的函数,并且不会切片也不会复制到X对象中。 在步骤中,这样做

  • 取消引用指针y ,其类型为Y* 解除引用会产生类型为Y左值表达式。 左值表达式实际上可以表示派生类型的对象,即使其静态类型是其基类之一。
  • 转换为X& ,这是对X的引用。 这将产生类型X左值表达式。
  • 调用该函数。

你原来的演员做了

  • 取消引用指针y
  • 结果表达式转换为X 这将导致复制操作到新的X对象。 结果表达式是静态类型X右值表达式。 表示的对象的动态类型也是 X ,与所有rvalue表达式一样。
  • 调用该函数。

转换always(*)创建一个您要转换为的类型的新对象,该对象是使用您正在转换的对象构造的。

转换为X *会创建一个新指针(即X *类型的对象)。 它具有与y相同的值,因此它仍指向Y类型的同一对象。

转换为X会创建一个新的X.它是使用*y构造的,但它与旧对象无关。 在您的示例中, foo()在此新“临时”对象上调用,而不是在y指向的对象上调用。

你是正确的,动态多态只适用于指针和引用,而不适用于对象,这就是为什么:如果你有一个指向X的指针,那么它指向的东西可能是X的子类。但是如果你有一个X,然后它是一个X,没有别的。 虚拟通话毫无意义。

(*)除非优化允许省略不改变结果的代码。 但是不允许优化改变调用foo()函数。

解除引用( *y部分)很好,但是强制转换( (X)部分)创建了一个特定于X类的新(临时)对象 - 这就是强制转换的含义 因此,对象必须具有来自类X的虚拟表 - 考虑到转换将删除子类中由Y添加的任何实例成员(实际上, X如何复制ctor可能知道它们?),所以它如果任何Y的覆盖都要执行,那么这可能是一场灾难 - 他们知道this是一个Y的实例,加上成员和所有......当知识是假的时候!

你投射指针的版本当然是完全不同的 - *XY*具有相同的位,所以它仍然指向一个完全有效的Y实例(当然,它指向y ,当然)。

可悲的事实是,为了安全起见,类的副本应该只作为参数调用该类的实例 - 而不是任何子类; 增加实例成员和c的损失太具破坏性了。 但确保这一点的唯一方法是遵循Haahr的优秀建议,“不要强化具体类”......尽管他正在撰写有关Java的文章,但建议至少与C ++一样好(有这个副本ctor“切片“问题另外! - )

我认为这仅仅是由于语言的指定方式。 引用和指针尽可能使用后期绑定,而对象使用早期绑定。 在每种情况下(我想象)都可以进行后期绑定,但是那样做的编译器不会遵循C ++规范。

我认为Darth Eru的解释是正确的,这就是为什么我认为C ++的行为方式如下:

代码(X)* y就像创建一个X类型的局部变量一样。编译器需要在堆栈上分配sizeof(X)空间,并抛弃Y类型对象中包含的任何额外数据,所以当你调用foo()它必须执行X版本。 编译器很难以一种让你调用Y版本的方式运行。

代码(X *)y就像创建指向对象的指针一样,编译器知道指向的对象是X或X的子类。在运行时,当你取消引用指针并用“ - > foo()调用foo时“确定对象的类,并使用正确的函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM