【问题标题】:Object slicing : pass Derived as Base by value - safe or dangerous?对象切片:按值传递 Derived as Base - 安全还是危险?
【发布时间】:2017-03-07 13:02:35
【问题描述】:

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

我阅读了一个关于 what is safe slicing VS dangerous slicing 的精彩链接。
以下是我可以总结的(粗略地说):-

  • 安全,当基本类型为 value 时(例如 A)。
  • 危险,当基本类型为 reference 时(例如 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 的行为与我的看法相矛盾。

  • 当类型为value警告
  • 当类型为 reference无警告(将 #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 错了?

【问题讨论】:

  • 实际上,切片只能在按值分配时成为问题,如果您使用pointers or references,您的对象将不会切片并且会正确地表现出多态性。
  • (已编辑)好点!你似乎是正确的....我现在明白了。谢谢!
  • @CoryKramer 如果您愿意,可以将其作为解决方案发布。 :)

标签: c++ resharper object-slicing


【解决方案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);
      }
};

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

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

void f(B1& b){ } 

没错,这就是多态对象的传递方式,通过将对象分配给基类类型的指针或引用。

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

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

【讨论】:

  • Try adding int n; to C
【解决方案2】:

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

当 f 为 f(B1& b) 时,不会发生切片。预计不会发出警告。

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

当 C 仅从 B1 派生时,它的整个状态都包含在基础子对象 B1 中。因此,转换实际上不会切掉任何数据。也许您的 IDE 认为在这种情况下,可能无意的转换不会成为问题。

我对对象切片有误解吗

您似乎认为将基础引用绑定到对象会分割基础子对象。它不是。 “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 
}

【讨论】:

    猜你喜欢
    • 2015-06-12
    • 2017-02-20
    • 2016-02-11
    • 2012-11-04
    • 1970-01-01
    • 1970-01-01
    • 2011-11-09
    • 1970-01-01
    相关资源
    最近更新 更多