是否有任何 C++ 对象切片效果的示例可能导致未定义的行为、内存泄漏或在其他正确的代码集中崩溃?例如,当 classA
和B
(继承自A
)是正确且合理的,但调用 avoid f(A a)
显然会导致讨厌的事情。
它是形成测试问题所必需的。目标是使用示例代码片段了解参与者是否意识到切片现象,该示例代码片段的正确性绝不能是意见问题。
是否有任何 C++ 对象切片效果的示例可能导致未定义的行为、内存泄漏或在其他正确的代码集中崩溃?例如,当 classA
和B
(继承自A
)是正确且合理的,但调用 avoid f(A a)
显然会导致讨厌的事情。
它是形成测试问题所必需的。目标是使用示例代码片段了解参与者是否意识到切片现象,该示例代码片段的正确性绝不能是意见问题。
如果A
确实“正确且合理”,那么切片(复制基本子对象)是明确定义的,不会导致您提到的任何问题。如果您希望副本的行为类似于B
.
如果A
不能正确复制,那么切片将导致复制该类型对象时出现的任何问题。例如,如果它有一个析构函数删除对象持有的指针,并且复制创建一个指向同一事物的新指针,那么当两个析构函数删除同一个指针时,您将获得未定义的行为。这不是切片本身的问题,而是切片对象的无效复制语义。
你总是可以构建这样一个例子
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();
}
当然A
,当复制构造函数/赋值运算符不检查不变量是否为真时,这可以在内部防止。
如果不变量比我的布尔值更隐含,那么这些东西可能很难看。
只有当您通过指针或对其基类的引用来操作派生类时,对象切片才是真正的问题。然后,派生类的附加数据保持不变,而基础部分的数据可能会被改变。这可能会破坏派生类的不变量。一个简单的例子见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
。