我正在研究对象切片何时/为何是危险的。
我读了一个关于什么是安全切片与危险切片的好链接。
以下是我可以总结的(粗略地说):-
- 当基类型为值时安全(例如
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的行为与我的信念相矛盾。
- 当类型为值时发出警告。
- 当类型为引用时没有警告(将 #2 从
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错了?
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);
}
};
在函数调用中,从派生自B1
的C
类型的变量c
对类型为B1
的变量b
的值初始化将执行签名为B1(const B1& rhs);
的B1
的复制构造函数,在此构造函数中(无论是否自动生成)中只有B1
中存在的字段将从rhs
复制, 所以其余的(来自B2
或C
的部分)将被切片。
当类型为引用时没有警告(将 #2 从 B1 更改为 B1&)。
void f(B1& b){ }
这是正确的,这就是多态对象应该传递的方式,通过将对象分配给基类类型的指针或引用。
当 C 仅从 B1 派生时,无论 #2 如何,总是没有警告。(B 或 B&)
这只是因为C
没有字段,所以在这种情况下没有切片。尝试将int n;
添加到C
,将再次显示警告。
当类型为引用时没有警告(将 #2 从 B1 更改为 B1&)。
当ff(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
}