我正在研究何时/为何对象切片很危险。
我读了一篇关于what is safe slicing VS dangerous slicing的精彩链接 以下是我可以总结的内容(粗略地说): -
A
)。 A&
)。 阅读完之后,我在 Visual Studio + Resharper (一个VS插件)中创建了一个测试代码。
我认为我的案例是安全。但是,我在标记的行#1
上收到了警告。
可能是非预期的对象切片值 - 从派生类C初始化
class B1{ int field1=0; };
class B2{ int field2=0; };
class C: public B1,public B2{ };
class Test{
void f(B1 b){ } #2
void f2(){
C c;
f(c);
//^ possibly unintended object slicing #1
}
};
Resharper 以与我的信念相反的方式行事。
B1
更改为B1&
)。C
仅来自B1
,无论#2
是什么。 (B
或B&
)可以归纳为表格: -
| #2=B1 | #2=B1&
==================================================
multi-inherit | warn* | no-warn*
--------------------------------------------------
inherit only from B1 | no-warn | no-warn
然而,这是我的期望: -
| #2=B1 | #2=B1&
===================================================================
multi-inherit | safe* | dangerous*
-------------------------------------------------------------------
inherit only from B1 | safe | safe
不一致性标有*
。
我是否误解了对象切片,或 Resharper 错了?
答案 0 :(得分:1)
当类型为引用时没有警告(将#2从B1更改为B1&)。
当f为f(B1& b)
时,则不会发生切片。预计不会发出警告。
当C仅来自B1时,始终没有警告,无论#2是什么。 (B或B&)
当C仅来自B1时,其整个状态包含在基础子对象B1中。因此,转换实际上没有数据被切掉。也许你的IDE认为在这种情况下,可能无意的转换不会成为问题。
我是否误解了对象切片
您似乎假设将基础引用绑定到对象会切割基础子对象。它不是。 "奸诈" (根据链接的答案)切片在通过引用分配对象时发生:
void f(B1& b){
b = C{}; // the B2 portion of the argument is sliced off;
// only B1 portion is assigned. If b refers to a
// C, then ((C&)b)::B2 remains unmodified.
// If that was unintentional, then you were affected
// by the treacherousness
}
答案 1 :(得分:1)
Resharper的行为与我的信念形成矛盾。
它以正确的方式行动,让我们看看:
当类型为值时发出警告。
这是代码:
class B1{ int field1=0; };
class B2{ int field2=0; };
class C: public B1,public B2{ };
class Test{
void f(B1 b){ }
void f2(){
C c;
f(c);
}
};
在函数调用中,从b
派生的类型为B1
的变量c
的类型C
的变量B1
的值初始化将执行B1
的签名为B1(const B1& rhs);
的复制构造函数,在此构造函数中(无论是否自动生成),B1
中仅存在的字段将从rhs
复制,因此其余部分(部分来自B2
或C
)将被切片。
当类型为引用时没有警告(将#2从B1更改为B1&)。
void f(B1& b){ }
这是正确的,这是多态对象应该如何传递,通过将对象分配给指针或基类类型的引用。
当C仅来自B1时,始终没有警告,无论#2是什么。 (B或B&)
那只是因为C
没有字段,所以在这种情况下没有切片。尝试将int n;
添加到C
,系统会再次显示警告。