对象切片:通过值传递派生为基数 - 安全还是危险?

时间:2017-03-07 13:02:35

标签: c++ resharper object-slicing

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

我读了一篇关于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 以与我的信念相反的方式行事。

    当类型为时,
  • 警告
  • 如果类型为参考,则
  • 无警告(将{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 错了?

2 个答案:

答案 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复制,因此其余部分(部分来自B2C)将被切片。

  

当类型为引用时没有警告(将#2从B1更改为B1&)。

void f(B1& b){ } 

这是正确的,这是多态对象应该如何传递,通过将对象分配给指针或基类类型的引用。

  

当C仅来自B1时,始终没有警告,无论#2是什么。 (B或B&)

那只是因为C没有字段,所以在这种情况下没有切片。尝试将int n;添加到C,系统会再次显示警告。