对象切片:按值将 Derived 作为 Base 传递 - 安全还是危险?
Object slicing : pass Derived as Base by value - safe or dangerous?
我正在研究 when/why 对象切片很危险。
我读了一篇很棒的 link 关于 what is safe slicing VS dangerous slicing。
这是我可以总结的(粗略地说):-
- Safe 当基本类型为 value 时(例如
A
)。
- 危险 当基本类型是 reference(例如
A&
)。
看完后,我在Visual Studio + Resharper(一个VS插件)中创建了一个测试代码。
我认为我的情况 安全 。但是,我在标记行 #1
.
处收到警告
possibly unintended object slicing value-initialized from derived class 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 的行为与我的信念相矛盾。
- 警告 当类型为 value.
当类型为 reference 时,- No warning(将 #2 从
B1
更改为 B1&
)。
- 总是没有警告 当
C
仅从 B1
派生时,无论 #2
是什么。 (B
或 B&
)
可以归纳为table :-
| #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 错了?
No warning when the type is reference (change #2 from B1 to B1&).
当 f 为 f(B1& b)
时,则不会发生切片。预计不会出现警告。
Always no warning when C derived from only B1, no matter #2 is. (B or B&)
当 C 仅派生自 B1 时,其整个状态包含在基础子对象 B1 中。因此实际上没有数据被转换切掉。也许您的 IDE 认为在这种情况下,可能的无意转换不会成为问题。
Do I misunderstand about object slicing
您似乎假设将基引用绑定到对象会分割基子对象。它不是。 "treacherous"(根据链接的答案)切片发生在通过引用分配给对象时:
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
}
Resharper acts in a contradict way from my belief.
它以正确的方式运行,让我们看看:
Warning when the type is value.
这是针对此代码的:
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
的部分)将被切片。
No warning when the type is reference (change #2 from B1 to B1&).
void f(B1& b){ }
没错,这就是传递多态对象的方式,方法是将对象分配给基本 class 类型的指针或引用。
Always no warning when C derived from only B1, no matter #2 is. (B or B&)
那只是因为 C
没有字段,所以在这种情况下没有切片。尝试将 int n;
添加到 C
并再次显示警告。
我正在研究 when/why 对象切片很危险。
我读了一篇很棒的 link 关于 what is safe slicing VS dangerous slicing。
这是我可以总结的(粗略地说):-
- Safe 当基本类型为 value 时(例如
A
)。 - 危险 当基本类型是 reference(例如
A&
)。
看完后,我在Visual Studio + Resharper(一个VS插件)中创建了一个测试代码。
我认为我的情况 安全 。但是,我在标记行 #1
.
possibly unintended object slicing value-initialized from derived class 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 的行为与我的信念相矛盾。
- 警告 当类型为 value. 当类型为 reference 时,
- No warning(将 #2 从
B1
更改为B1&
)。 - 总是没有警告 当
C
仅从B1
派生时,无论#2
是什么。 (B
或B&
)
可以归纳为table :-
| #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 错了?
No warning when the type is reference (change #2 from B1 to B1&).
当 f 为 f(B1& b)
时,则不会发生切片。预计不会出现警告。
Always no warning when C derived from only B1, no matter #2 is. (B or B&)
当 C 仅派生自 B1 时,其整个状态包含在基础子对象 B1 中。因此实际上没有数据被转换切掉。也许您的 IDE 认为在这种情况下,可能的无意转换不会成为问题。
Do I misunderstand about object slicing
您似乎假设将基引用绑定到对象会分割基子对象。它不是。 "treacherous"(根据链接的答案)切片发生在通过引用分配给对象时:
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
}
Resharper acts in a contradict way from my belief.
它以正确的方式运行,让我们看看:
Warning when the type is value.
这是针对此代码的:
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
的部分)将被切片。
No warning when the type is reference (change #2 from B1 to B1&).
void f(B1& b){ }
没错,这就是传递多态对象的方式,方法是将对象分配给基本 class 类型的指针或引用。
Always no warning when C derived from only B1, no matter #2 is. (B or B&)
那只是因为 C
没有字段,所以在这种情况下没有切片。尝试将 int n;
添加到 C
并再次显示警告。