01 May 2023

C++ 核心指南目录

“Don’t slice”

理由

所谓的切片,就是在赋值或初始化的时候,只 copy 对象的一部分数据。因为对象要整体考虑,这种情况经常导致错误。在某些情况,会导致奇怪的结果。

例子

class Shape { /* ... */ };
class Circle : public Shape { /* ... */ Point c; int r; };

Circle c {{0, 0}, 42};
Shape s {c};    // copy construct only the Shape part of Circle
s = c;          // or copy assign only the Shape part of Circle

void assign(const Shape& src, Shape& dest)
{
    dest = src;
}
Circle c2 {{1, 1}, 43};
assign(c, c2);   // oops, not the whole state is transferred
assert(c == c2); // if we supply copying, we should also provide comparison,
                 // but this will likely return false

以上代码的结果是无意义的,因为圆心和半径部分没用从 c 复制到 s 。为了避免这种情况方式,第一步在基类 Shape的定义中禁止这种操作。

其他选项

如果你确实需要切片 slice,你可以显式地定义这个操作。从而避免代码读者产生疑惑。比如:

class Smiley : public Circle {
  public:
    Circle copy_circle();
    // ...
};

Smiley sm { /* ... */ };
Circle c1 {sm};  // ideally prevented by the definition of Circle
Circle c2 {sm.copy_circle()};

强化

  • 对于产生切片的地方发出警告