C++ slicing causing leak / undefined behavior / crash

此生再无相见时 提交于 2019-11-29 12:50:29

If A is indeed "correct and sound", then slicing (copying the base sub-object) is well-defined and will not cause any of the problems you mention. The only problem it will cause is unexpected behaviour, if you expect the copy to behave like B.

If A is not correctly copyable, then slicing will cause whatever problems arise from copying objects of that type. For example, if it has a destructor which deletes a pointer held by object, and copying creates a new pointer to the same thing, then you will get undefined behaviour when both destructors delete the same pointer. This isn't a problem with slicing as such, but with invalid copy semantics of the sliced object.

You can always construct such an example

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

Of course, this could be prevented inside A, when the copy constructor/assignment operator doesn't checks whether invariant is true.

If the invariant is more implicit than my boolean value, these things can be very tricky to see.

object slicing is only really a problem if you manipulate derived classes through pointers or references to their base class. Then, the additional data of the derived class remain unchanged, while those in the base part may get altered. This may break invariants of the derived class. For a simple example see http://en.wikipedia.org/wiki/Object_slicing

However, I would consider this a design flaw (of the derived class). If accessing a class by any legal method, including those taken pointer or reference argument to the base class, can break its invariants, the class is badly designed. One way to avoid that is to declare the base private, so that the derived class cannot legally be accessed via its base.

An example would be:

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

From this example it's also obvious that this is a simple design flaw in B. In this example, providing

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

doesn't solve the problem, as

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

still breaks the invariant. The only solution here is to declare the base A private or, better, make A a private member, rather than base, of B.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!