2

我遇到了一个问题,即强制转换为派生类可以解决问题。我在 SO 上找到了一个答案,说它可以导致 UB,对其进行测试,它既可以编译又可以正常工作。它是未定义的行为吗?如果是解决这个问题的正确方法是什么?

class A
{
public:
    A(){};
    ~A(){}
};

class B : public A
{
public:
    B(){};
    ~B(){}
    void Show() { std::cout << "Show" << std::endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    A a;
    B* b = static_cast<B*>(&a);
    b->Show();

    return 0;
}
4

3 回答 3

9

只要指向基类型的指针实际上指向派生类型的实例,那么根据 C++ 标准,这种用法就不是未定义的。但是,在您的代码示例中,指针b不指向的实例B或其任何派生类型(没有),它指向的实例A。因此,您的代码实际上确实调用了未定义的行为。

我在 SO 上找到了一个答案,说它可以导致 UB,对其进行测试,它既可以编译又可以正常工作。

某些代码编译并正常工作的事实并不排除代码调用未定义行为的可能性,因为未定义行为包括“似乎可以工作”。您应该避免未定义行为的原因是因为无法保证下次调用 UB 时它会以相同的方式工作。

它是未定义的行为吗?如果是解决这个问题的正确方法是什么?

在您的示例中,是的,这是未定义的行为。正确的方法取决于您的代码实际上应该做什么,因为您提供的示例充其量只是一个学术示例。

为了清楚起见,对main()函数的以下修改具有明确定义的行为,并且 C++ 标准明确允许:

B objectB;
A* ptrA = &objectB;

B* b = static_cast<B*>(ptrA);
b->Show();

在这里,它的定义很好,因为指针ptrA实际上指向 的实例B,即使指针本身具有 type A*。在上面的示例中,将 fromA*转换为然后在转换的指针上B*调用 's 函数之一将起作用。B不同之处在于,在您问题的示例中,b实际上并不指向B.


相关条款(强调我的):

C++ 标准 5.2.9/8 静态转换 [expr.static.cast]

如果从“ pointer to _ _ _ ” 到“指向 ” 的指针存在BB DDBDB(4.10),cv2与cv1具有相同的 cv限定或大于cv限定,并且不是 的虚拟基类。空指针值 (4.10) 被转换为目标类型的空指针值。如果“指向 cv1 的指针”类型的右值指向BDBB那实际上是 type 对象的子对象D,结果指针指向 type 的封闭对象D。否则,强制转换的结果是未定义的。

于 2013-02-16T20:54:24.323 回答
2

您可以将指向确实指向派生实例的基础对象的指针转换为指向派生对象的指针。

但是,在您的代码中,指向的对象&a不是派生对象,而是基础对象,您所做的确实是未定义的行为。

在实现中,如果类没有虚函数或基并且派生对象不添加任何数据成员而只是添加方法,我知道它应该“工作”。仍然不能正式保证它可以正常工作。

只是不要那样做。

于 2013-02-16T20:54:54.517 回答
2

如果指向的对象确实是 a ,则允许强制转换(因此有效)B。在您的示例中,它很简单A,因此您会得到未定义的行为。

这留下了一个问题,为什么它有效?它有效(或似乎有效),因为您调用的方法没有访问该对象。B如果您添加一些成员变量并尝试访问它们,Show()或者您创建Show一个虚函数,它更有可能失败。但无论如何它是UB,所以基本上任何事情都可能发生。

于 2013-02-16T20:56:50.083 回答