是否有任何C ++对象切片效果的例子,它会导致未定义的行为,内存泄漏或在其他正确的代码集中崩溃?例如,当课程A
和B
(继承自A
)正确且合理时,但调用void f(A a)
可能会导致令人讨厌的事情。
需要形成测试问题。目标是使用示例代码片段来了解参与者是否知道切片现象,其正确性不一定是意见问题。
答案 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
的私有成员,而不是基础成员。