对象切片:通过按值派生为 Base - 安全还是危险?



我正在研究对象切片何时/为何是危险的。

我读了一个关于什么是安全切片与危险切片的好链接。
以下是我可以总结的(粗略地说):-

  • 当基类型为安全(例如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是。(BB&)

它可以通过总结成一个表格:-

|    #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);
}
};

在函数调用中,从派生自B1C类型的变量c对类型为B1的变量b的值初始化将执行签名为B1(const B1& rhs);B1的复制构造函数,在此构造函数中(无论是否自动生成)中只有B1中存在的字段将从rhs复制, 所以其余的(来自B2C的部分)将被切片。

当类型为引用时没有警告(将 #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 
}

最新更新