Mar*_*cze 0 c++ polymorphism inheritance casting
我有以下类层次结构:
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;
}
};
Run Code Online (Sandbox Code Playgroud)
我有一个IControl抽象类和一个ControlBase类.ControlBase类不从IControl继承,但我知道每个IControl实现都将从ControlBase派生.
我有以下测试代码,其中我使用dynamic_cast和C样式转换将IControl -reference转换为ControlBase(因为我知道它是从它派生的):
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.
}
Run Code Online (Sandbox Code Playgroud)
只有dynamic_cast正常工作,其他两个强制转换才会返回略有不同的内存地址,GetType()函数会返回不正确的值.
这是什么原因?C风格的演员最终是否使用了reinterpret_cast?它与多态对象在内存中的对齐方式有关吗?
我认为你的例子中的类名有点令人困惑.让我们给他们打电话Interface,Base和Impl.请注意,Interface和Base是无关的.
C++标准定义了C风格的强制转换,在[expr.cast]中称为"显式类型转换(强制转换符号)".您可以(也许应该)阅读整个段落,以确切了解C样式转换的定义方式.对于OP中的示例,以下内容就足够了:
C风格可以执行[expr.cast]/4之一的转换:
const_caststatic_caststatic_cast 其次是 const_castreinterpret_castreinterpret_cast 其次是 const_cast此列表的顺序很重要,因为:
如果转换可以用上面列出的多种方式解释,则使用列表中首先出现的解释,即使由该解释产生的转换是格式错误的.
我们来看看你的例子
Impl impl;
Interface* pIntfc = &impl;
Base* pBase = (Base*)pIntfc;
Run Code Online (Sandbox Code Playgroud)
A const_cast不能使用,列表中的下一个元素是a static_cast.但是类Interface和Base是不相关的,因此不存在static_cast可以从转换Interface*到Base*.因此,使用a reinterpret_cast.
附加说明:您的问题的实际答案是:由于dynamic_cast上面的列表中没有,C风格的演员表演从不像adynamic_cast.
实际地址如何变化不是C++语言定义的一部分,但我们可以举例说明如何实现它:
具有至少一个虚函数(继承或拥有)的类的每个对象包含(读取:在该示例中可以包含)指向vtable的指针.如果它从多个类继承虚函数,它包含多个指向vtable的指针.由于空基类优化(没有数据成员),实例Impl可能如下所示:
+=Impl=======================================+ | | | +-Base---------+ +-Interface---------+ | | | vtable_Base* | | vtable_Interface* | | | +--------------+ +-------------------+ | | | +============================================+
现在,例子:
Impl impl;
Impl* pImpl = &impl;
Interface* pIntfc = pImpl;
Base* pBase = pImpl;
Run Code Online (Sandbox Code Playgroud)
+=Impl=======================================+ | | | +-Base---------+ +-Interface---------+ | | | vtable_Base* | | vtable_Interface* | | | +--------------+ +-------------------+ | | ^ ^ | +==|==================|======================+ ^ | | | +-- pBase +-- pIntfc | +-- pimpl
如果你做了一个reinterpret_cast,结果是实现定义的,但它可能导致这样的事情:
Impl impl;
Impl* pImpl = &impl;
Interface* pIntfc = pImpl;
Base* pBase = reinterpret_cast<Base*>(pIntfc);
Run Code Online (Sandbox Code Playgroud)
+=Impl=======================================+ | | | +-Base---------+ +-Interface---------+ | | | vtable_Base* | | vtable_Interface* | | | +--------------+ +-------------------+ | | ^ | +=====================|======================+ ^ | | +-- pIntfc | | +-- pimpl +-- pBase
即地址未更改,pBase指向Interface对象的子Impl对象.
请注意,取消引用指针pBase已将我们带到UB-land,标准没有指定应该发生什么.在此示例性实现中,如果您调用pBase->GetType(),vtable_Interface*则使用包含SomeMethod该条目的函数,并调用该函数.这个函数不返回任何东西,所以在这个例子中,召唤鼻子恶魔并接管世界.或者从堆栈中取一些值作为返回值.