C++ 复杂的动态c++;

C++ 复杂的动态c++;,c++,polymorphism,language-lawyer,dynamic-cast,cross-cast,C++,Polymorphism,Language Lawyer,Dynamic Cast,Cross Cast,我用C++编写了以下案例: 抽象基类Abstract1和Abstract2。他们没有关系 源于Abstract1和Abstract2 我在一个编译单元中,没有关于类Foo(没有声明,没有定义)的信息。只有Abstract1和Abstract2是已知的。 (实际上,Foo甚至是在DLL中定义的) 动态强制转换是否允许从Abstract1*强制转换到Abstract2*?这是标准吗?是的,它会起作用 dynamic_cast基于。RTTI在此提供的信息足以确定指向对象的实际动态类型。根据定义,R

我用C++编写了以下案例:

  • 抽象基类
    Abstract1
    Abstract2
    。他们没有关系
  • 源于
    Abstract1
    Abstract2
我在一个编译单元中,没有关于类
Foo
(没有声明,没有定义)的信息。只有
Abstract1
Abstract2
是已知的。 (实际上,Foo甚至是在DLL中定义的)

动态强制转换是否允许从
Abstract1*
强制转换到
Abstract2*
?这是标准吗?

是的,它会起作用

dynamic_cast
基于。RTTI在此提供的信息足以确定指向对象的实际动态类型。根据定义,RTTI是一个运行时概念,指向对象的动态类型也是如此(编写所述cast的编译单元中没有Foo的定义这一事实是一个编译时概念,与此无关)

  • 如果指向的对象实际上是一个Foo,则动态\u转换将在运行时成功
  • 如果它不是指向从Abstract2派生的对象的指针,它将失败(返回空指针)
细节
dynamic_cast
的一个可能实现是在对象内存布局的开始处查找一个特殊成员(或者它可以存储在v表中)。此结构可以包含一个值,该值标识对象的动态类型。 在某个地方,编译器会生成一个静态表,复制有关程序继承图的所有信息。 在运行时,cast将提取实例的类型标识符,并对照静态表进行检查。如果此标识符引用从Abstract2派生的类型,则强制转换是有意义的(并且代码可以返回一个指向对象的
Abstract2
接口的指针)

即使是这种幼稚的实现也不需要编译单元中的
Foo
知识
cast是书面的。

您描述的是所谓的交叉cast。对于
dynamic\u cast(v)
,标准在[expr.dynamic.cast]/8中规定

如果
C
T
指向或引用的类类型,则运行时 检查逻辑上执行如下操作:

  • 如果在
    v
    指向(引用)的最派生对象中,
    v
    指向(引用)一个
    C
    对象[…]的公共基类子对象

  • 否则,如果
    v
    指向(引用)最派生对象的
    公共
    基类子对象,以及最派生对象的类型 有一个类型为
    C
    的基类,该基类是明确的,
    public
    结果点(指)是最派生的
    C
    子对象 对象。

即使在包含cast的翻译单元中没有关于
Foo
存在的信息,这也会起作用


你也应该去看看。

好吧,你可以试一下

#包括
结构IBase1
{
虚拟void foo()=0;
虚拟~IBase1(){}
};
结构IBase2
{
虚拟空心条()=0;
虚拟~IBase2(){}
};
派生结构:IBase1、IBase2
{
void foo(){}
无效条(){}
};
int main()
{
导出d;
IBase1*ptr=&d;
断言(动态_cast(ptr));
断言(动态_cast(ptr));
}
//编译成功
这就是证据:

[C++11:5.2.7/8]:
如果
C
T
指向或引用的类类型,则运行时检查按如下逻辑执行:

  • 如果在由
    v
    指向(引用)的最派生对象中,
    v
    指向(引用)一个
    C
    对象的
    public
    基类子对象,并且如果从由
    v
    指向(引用)的子对象仅派生一个
    C
    类型的对象,则结果点(引用)到
    C
    对象
  • 否则,如果
    v
    指向(引用)最派生对象的公共基类子对象,并且最派生对象的类型具有类型为
    C
    的基类,即明确且
    public
    ,则结果指向(引用)到最派生对象的
    C
    子对象。

  • 否则,运行时检查将失败

通俗地说,我们称之为交叉投射


该语言没有规定“当前”翻译单元中已知最派生对象的类型;这取决于实现,在常见的“虚拟表”模型中,确实如此。

对于此代码:

void func(Abstract1* a1)
{
    Abstract2* a2 = dynamic_cast<Abstract2*>(a1);
    ...
}
void func(抽象1*a1)
{
Abstract2*a2=动态铸型(a1);
...
}
你在问:

void func(Abstract1* a1)
{
    Abstract2* a2 = dynamic_cast<Abstract2*>(a1);
    ...
}
如果
a1
指向
Foo
对象,动态强制转换是否会返回有效的对象指针

答案是肯定的:

void func(Abstract1* a1)
{
    Abstract2* a2 = dynamic_cast<Abstract2*>(a1);
    ...
}
  • 在运行期间,动态强制转换将
    a1
    的V-Table标识为
    类Foo
    的V-Table
  • 由于
    class Foo
    继承自
    class Abstract2
    ,因此动态强制转换将返回有效指针

是的,这是合法的。这有时被称为交叉转换(googlable)。请注意限制,即
Foo
的定义在转换点不可用。@NeilKirk这不是必需的。没有理由这么做。@NeilKirk:不要紧,一些有效的东西可能只是未定义的行为。尝试不会带来任何好处proof@galinette:这就是为什么我的答案以标准文本结尾。@galinette:我不知道你在说什么!;)