C++ slicing causing leak / undefined behavior / crash

大城市里の小女人 提交于 2019-11-28 06:35:19

问题


Is there any example of the C++ object slicing effect which can cause undefined behavior, memory leak or crash in an otherwise correct set of code? For example when class A and B (inherited from A) are correct and sound, but calling a void f(A a) demonstrably causes nasty things.

It is needed for forming a test question. The goal is to know if the participant is aware of the slicing phenomenon or not, using an example code snippet whose correctness must not be a matter of opinion.


回答1:


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.




回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/13606143/c-slicing-causing-leak-undefined-behavior-crash

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