C ++切片导致泄漏/未定义的行为/崩溃

时间:2012-11-28 13:26:03

标签: c++ object-slicing

是否有任何C ++对象切片效果的例子,它会导致未定义的行为,内存泄漏或在其他正确的代码集中崩溃?例如,当课程AB(继承自A)正确且合理时,但调用void f(A a)可能会导致令人讨厌的事情。

需要形成测试问题。目标是使用示例代码片段来了解参与者是否知道切片现象,其正确性不一定是意见问题。

3 个答案:

答案 0 :(得分:4)

如果A确实“正确且合理”,那么切片(复制基础子对象)是明确定义的,不会引起您提到的任何问题。如果您希望副本的行为类似于B,那么它将导致的唯一问题是意外行为。

如果A无法正确复制,则切片将导致复制该类型对象时出现的任何问题。例如,如果它有一个析构函数删除对象持有的指针,并且复制创建一个指向同一事物的新指针,那么当两个析构函数删除相同的指针时,您将得到未定义的行为。这不是切片这样的问题,而是切片对象的无效复制语义。

答案 1 :(得分:1)

你总是可以构建这样一个例子

struct A {
  A() : invariant(true) {}
  virtual void do_sth() { assert(invariant); }
protected:
  bool invariant;
};

struct B : A {
  B() { invariant=false; }
  virtual void do_sth() { }
};

void f(A a)
{
  a.do_sth();
}

当然,当复制构造函数/赋值运算符不检查invariant是否为真时,可以在A内阻止这种情况。

如果不变量比我的布尔值更隐式,那么这些事情可能会非常棘手。

答案 2 :(得分:1)

如果通过指针或对其基类的引用来操作派生类,那么

对象切片才是真正的问题。然后,派生类的附加数据保持不变,而基础部分中的附加数据可能会被更改。这可能会破坏派生类的不变量。有关简单示例,请参阅http://en.wikipedia.org/wiki/Object_slicing

但是,我认为这是设计缺陷(派生类的)。如果通过任何合法方法访问类,包括那些采用指针或引用参数到基类的类,可以打破它的不变量,那么类的设计很糟糕。避免这种情况的一种方法是声明基类private,这样派生类就无法通过其基础合法访问。

一个例子是:

class A
{
  int X;
  A(int x) : X(x) {}
  void doubleX() { X+=X; }
  /* ... */
};

class B : public A
{
  int X_square;
  B(int x) : A(x), X_square(x*x) {}
  /* ... */      
};

B b(3);
B.doubleX();   /// B.X = 6 but B.X_square=9

从这个例子中可以明显看出,这是B中一个简单的设计缺陷。在这个例子中,提供

void B::doubleX() { A::doubleX(); X_squared=X*X; }

没有解决问题,如

A&a=b;
a.doubleX();

仍然打破了不变量。这里唯一的解决方案是声明基础A私有,或者更好地使A成为B的私有成员,而不是基础成员。