问题
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