gpt4 book ai didi

c++ - 为什么 C 风格的转换行为与 dynamic_cast 不同?

转载 作者:太空宇宙 更新时间:2023-11-04 14:44:44 24 4
gpt4 key购买 nike

我有以下类层次结构:

class IControl
{
virtual void SomeMethod() = 0; // Just to make IControl polymorphic.
};

class ControlBase
{
public:
virtual int GetType() = 0;
};

class ControlImpl : public ControlBase, public IControl
{
public:
virtual void SomeMethod() { }

virtual int GetType()
{
return 1;
}
};

我有一个IControl 抽象类和一个ControlBase 类。 ControlBase 类不继承自 IControl,但我知道每个 IControl 实现都将派生自 ControlBase。

我有以下测试代码,其中我使用 dynamic_castIControl 引用转换到 ControlBase(因为我知道它派生自它) >,还有 C 风格 转换:

int main()
{
ControlImpl stb;
IControl& control = stb;

ControlBase& testCB1 = dynamic_cast<ControlBase&>(control);
ControlBase& testCB2 = (ControlBase&)control;
ControlBase* testCB3 = (ControlBase*)&control;

std::cout << &testCB1 << std::endl;
std::cout << &testCB2 << std::endl;
std::cout << testCB3 << std::endl;
std::cout << std::endl;
std::cout << testCB1.GetType() << std::endl; // This properly prints "1".
std::cout << testCB2.GetType() << std::endl; // This prints some random number.
std::cout << testCB3->GetType() << std::endl; // This prints some random number.
}

只有 dynamic_cast 正常工作,其他两个转换返回略有不同的内存地址,并且 GetType() 函数返回不正确的值。

具体原因是什么? C 风格的强制转换最终会使用 reinterpret_cast 吗?与多态对象在内存中的对齐方式有关吗?

最佳答案

我认为您示例中的类名有点令人困惑。我们称它们为 InterfaceBaseImpl。请注意,InterfaceBase 不相关

C++ 标准定义了 C 风格的强制转换,在 [expr.cast] 中称为“显式类型转换(强制转换符号)”。您可以(也许应该)阅读整段内容以准确了解 C 风格的转换是如何定义的。对于 OP 中的示例,以下内容就足够了:

C 风格可以执行 [expr.cast]/4 之一的转换:

  • const_cast
  • static_cast
  • static_cast 后跟 const_cast
  • 重新解释_cast
  • reinterpret_cast 后跟 const_cast

这个列表的顺序很重要,因为:

If a conversion can be interpreted in more than one of the ways listed above, the interpretation that appears first in the list is used, even if a cast resulting from that interpretation is ill-formed.

让我们检查一下您的示例

Impl impl;
Interface* pIntfc = &impl;
Base* pBase = (Base*)pIntfc;

无法使用 const_cast,列表中的下一个元素是 static_cast。但是 InterfaceBase不相关,因此没有static_cast 可以从 Interface*Base*。因此,使用了reinterpret_cast

补充说明:您问题的实际答案是:由于上面列表中没有 dynamic_cast,C 风格的转换从不表现得像 dynamic_cast


实际地址如何变化不是C++语言定义的一部分,但我们可以举一个例子说明它是如何实现的:

具有至少一个虚函数(继承的或自己的)的类的每个对象都包含(读作:可以包含,在这个例子中)一个指向 vtable 的指针。如果它从多个类继承虚函数,它包含多个指向 vtables 的指针。由于空基类优化(没有数据成员),Impl 的实例可能如下所示:

+=Impl=======================================+|                                            ||  +-Base---------+   +-Interface---------+  ||  | vtable_Base* |   | vtable_Interface* |  ||  +--------------+   +-------------------+  ||                                            |+============================================+

Now, the example:

     Impl  impl;

Impl* pImpl = &impl;
Interface* pIntfc = pImpl;
Base* pBase = pImpl;
+=Impl=======================================+|                                            ||  +-Base---------+   +-Interface---------+  ||  | vtable_Base* |   | vtable_Interface* |  ||  +--------------+   +-------------------+  ||  ^                  ^                      |+==|==================|======================+^  |                  ||  +-- pBase          +-- pIntfc|+-- pimpl

If you instead do a reinterpret_cast, the result is implementation-defined, but it could result in something like this:

     Impl  impl;

Impl* pImpl = &impl;
Interface* pIntfc = pImpl;
Base* pBase = reinterpret_cast<Base*>(pIntfc);
+=Impl=======================================+|                                            ||  +-Base---------+   +-Interface---------+  ||  | vtable_Base* |   | vtable_Interface* |  ||  +--------------+   +-------------------+  ||                     ^                      |+=====================|======================+^                     ||                     +-- pIntfc|                     |+-- pimpl             +-- pBase

即地址不变,pBase指向Impl对象的Interface子对象。

请注意,取消引用指针 pBase 已经将我们带到了 UB-land,标准没有指定应该发生什么。在此示例性实现中,如果调用 pBase->GetType(),将使用 vtable_Interface*,其中包含 SomeMethod 条目,并且函数被调用。此函数不返回任何内容,因此在此示例中,鼻恶魔被召唤并接管了世界。或者从栈中取出一些值作为返回值。

关于c++ - 为什么 C 风格的转换行为与 dynamic_cast 不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18036138/

24 4 0
文章推荐: html - 需要

填充

文章推荐: c++ - 数学计算 - float
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com