C++ 当您使用多重继承时,静态强制转换何时是安全的?

C++ 当您使用多重继承时,静态强制转换何时是安全的?,c++,dynamic-cast,static-cast,C++,Dynamic Cast,Static Cast,我发现自己处于这样一种境地:我知道某件东西是什么类型的。该类型是三个(或更多)继承级别之一。我调用factory,它返回B*,但是T要么是类型的最高级别(如果我的代码知道它是什么),要么是第二级别 无论如何,我在模板中做了一个静态\u cast,这是错误的。我的问题是我什么时候可以安全地进行静态施放?有过这样的时刻吗?我在本例中这样做是因为当我意外地将T作为某种古怪的东西(已经发生并且)dynamic cast忽略(并返回null)时,我宁愿得到编译错误。但是,当我知道正确的类型时,指针没有进行

我发现自己处于这样一种境地:我知道某件东西是什么类型的。该类型是三个(或更多)继承级别之一。我调用factory,它返回
B*
,但是T要么是类型的最高级别(如果我的代码知道它是什么),要么是第二级别

无论如何,我在模板中做了一个
静态\u cast
,这是错误的。我的问题是我什么时候可以安全地进行静态施放?有过这样的时刻吗?我在本例中这样做是因为当我意外地将T作为某种古怪的东西(已经发生并且)dynamic cast忽略(并返回null)时,我宁愿得到编译错误。但是,当我知道正确的类型时,指针没有进行调整,导致指针损坏。我不知道为什么在这种情况下允许静态强制转换

我什么时候可以安全地使用静态_-cast进行向下投射?有过这样的情况吗?现在看来,使用
static\u cast
(当目的是向下转换时)似乎总是错误的

好吧,我知道怎么复制了

#include <iostream>
struct B { virtual void f1(){} };
struct D1 : B {int a;};
struct D2 : B {int a, b; };
struct DD : D1, D2 {};

int main(){
void* cptr = new DD(); //i pass it through a C interface :(
B*  a = (B*)cptr;
D2* b = static_cast<D2*>(a); //incorrect ptr
D2* c = dynamic_cast<D2*>(a); //correct ptr
std::cout << a << " " <<b << " " <<c;
}
#包括
结构B{virtualvoidf1(){}};
结构D1:B{int a;};
结构D2:B{inta,B;};
结构DD:D1,D2{};
int main(){
void*cptr=new DD();//我通过C接口传递它:(
B*a=(B*)cptr;
D2*b=静态_转换(a);//ptr不正确
D2*c=动态_转换(a);//正确的ptr

std::cout如果
派生的
基类
作为公共(或其他可访问的)基类,并且
d
类型为
派生的*
,那么
静态转换(d)
向上转换的

这在技术上总是安全的

而且通常是不必要的,除非有隐藏(阴影)方法的情况


干杯&hth.,

如果您确定对象实际上是该类的实例,则可以安全地向上投射

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

int main()
{
    Base* b = new Derived1;

    Derived1* d1 = static_cast<Derived1*>(b); // OK
    Derived2* d2 = static_cast<Derived2*>(b); // Run-time error - d isn't an instance of Derived2
}
类基{};
类Derived1:公共基{};
类Derived2:公共基{};
int main()
{
基本*b=新衍生的1;
Derived1*d1=静态_转换(b);//确定
Derived2*d2=static_cast(b);//运行时错误-d不是Derived2的实例
}
交叉播放:

struct Base1 { virtual void f1(); };
struct Base2 { virtual void f2(); };
struct Derived : Base1, Base2 {};

Base1* b1 = new Derived();
Base2* b2 = dynamic_cast<Base2*>(b1);
struct Base1{virtual void f1();};
结构Base2{virtualvoid f2();};
派生结构:Base1,Base2{};
Base1*b1=新派生的();
Base2*b2=动态_铸造(b1);
需要使用
dynamic_-cast
,不能使用
static_-cast
static_-cast
应该会导致编译时错误)。
dynamic_-cast
如果任一基类不是多态的(虚拟函数的存在不是可选的),也会失败


请参见

此行存在问题:

B*  a = (B*)cptr;
如果将某个对象转换为空指针,则在执行任何其他转换之前,必须将其转换回第一次转换的相同类型。如果存在多个不同类型的对象必须经过同一个空指针的情况,则在转换为空指针之前,需要先将其转换为公共类型

int main(){
  B *bptr = new DD; // convert to common base first (won't compile in this case)
  void* cptr = bptr; // now pass it around as a void pointer
  B*  a = (B*)cptr; // now back to the type it was converted from
  D2* b = static_cast<D2*>(a); // this should be ok now
  D2* c = dynamic_cast<D2*>(a);  // as well as this
  std::cout << a << " " <<b << " " <<c;
}
intmain(){
B*bptr=new DD;//首先转换为公共基(在这种情况下不会编译)
void*cptr=bptr;//现在将其作为void指针传递
B*a=(B*)cptr;//现在回到它转换的类型
D2*b=static_cast(a);//现在应该可以了
D2*c=dynamic_cast(a);//以及这个

std::cout交叉播放根本不需要动态播放

   struct Base1 { virtual void f1(); };
   struct Base2 { virtual void f2(); };
   struct Derived : Base1, Base2 {};

   Base1* b1 = new Derived();

   // To cast it to a base2 *, cast it first to a derived *
   Derived *d = static_cast<Derived *>(b1);
   Base2 *b2 = static_cast<Base2 *>(d);
struct Base1{virtual void f1();};
结构Base2{virtualvoid f2();};
派生结构:Base1,Base2{};
Base1*b1=新派生的();
//若要将其强制转换为base2*,请先将其强制转换为派生*
导出*d=静态_铸造(b1);
Base2*b2=静态_铸造(d);
只是为了完整(知道我有点晚了,只是为了像我这样的迟到读者……):

如果使用正确,可以应用静态投影

首先,简单的情况是:

struct D1 { }; // note: no common base class B!
struct D2 { };
struct DD : D1, D2 { };
您可以从
D1*
D2*
通过中间向下转换到
DD*

D1* d1 = new DD();
D2* d2 = static_cast<DD*>(d1);
现在这个强制转换已经很危险了!这里实际实现的是一个重新解释的强制转换:

B* a = reinterpret_cast<B*>(cptr);
单独地:DD有两个继承的B实例。如果D1和D2都从B继承,那么它就可以工作了(
struct D1/2:virtual B{};
——不要与B/D1/D2是虚拟类混淆!)

关于这件事,最重要的一点是:你绝对需要在下抛时使用与上抛相同的钻石刃


结论:是的,使用静态强制转换是可能的,如果所涉及的类不是虚拟的(注意:与虚拟继承不同!),这是唯一的选择。但是在正确执行时很容易失败,有时甚至是不可能的(例如,如果在
std::vector
)中存储了指向任意派生类型的基类型指针,那么通常情况下,由表示的是更安全的一个(前提是虚拟数据类型可用;如果是,在前面提到的向量示例中,它是唯一的解决方案!).

请更具体一点。你能提供一个代码示例吗?我想我大致知道你在说什么,但我不确定。一般来说,如果你是向上转换到合适的类型,那么进行静态转换是“安全的”。@Vaughn:我真的不能显示代码(这是我在工作中遇到的问题)但其中一个问题是多重继承和使用类作为接口,所有使用“向上转换”一词的人都需要阅读:您能给出一个代码示例来演示这个问题吗?您提出了一些新的只是为了演示的东西。哎哟,这看起来像是一个虚拟继承的例子。这几乎总是会变得非常混乱,几乎总是可以避免的-您确定您真的需要这个吗?例如,与其从D1和D2继承DD,为什么不使用composi选项?(
struct DD:B{D1 D1;D2 D2;};
)错误。使用多重继承时,D1和D2可以从基继承。您的条件为true,但对Dn的静态强制转换可能不正确,并且是
void* cptr = new DD();
B* a = (B*)cptr;
B* a = reinterpret_cast<B*>(cptr);
B* a = new DD(); //ambigous!
B* b1 = static_cast<D1*>(new DD());
B* b2 = static_cast<D2*>(new DD());
D2* bb1 = static_cast<DD*>(static_cast<D1*>(b1));
D1* bb2 = static_cast<DD*>(static_cast<D2*>(b2));