[英]C++ light switch with two buttons: if bar, do x & set foo=1; if thing and foo=1, do y & set foo=0
[英]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
的实例,加上成员和所有......当知识是假的时候!
你投射指针的版本当然是完全不同的 - *X
与Y*
具有相同的位,所以它仍然指向一个完全有效的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.