动态转换为类型的安全方法?

问题描述 投票:1回答:4

我的C ++有点生疏,我不记得标准中的所有内容。

我有一个void*。在一个特定的函数中,它是继承Alpha的类或继承Beta的类。两个基类都有虚函数。但是我似乎无法分辨哪个是哪个

class Alpha {
public:
    virtual void Speak() { printf("A"); }
};
class Beta {
public:
    virtual void Speak() { printf("B"); }
};
int main(){
    //BAD CODE WILL PRINT TWICE
    void *p = new Alpha;
    Alpha*a = dynamic_cast<Alpha*>((Alpha*)p);
    Beta*b = dynamic_cast<Beta*>((Beta*)p);
    if(a)
        a->Speak();
    if(b)
        b->Speak();
    return 0;
}

我怎么弄清楚哪个班级是哪个?此代码库中有100个类转换为void。他们中的大多数都继承了5个基类,但我并不急于找到。在使用动态强制转换之前,唯一的解决方案是继承自class Dummy {public: virtual void NoOp(){}};和强制转换为Dummy之类的东西吗?这样安全吗?我希望有更好的解决方案,但我想不出别的。

c++ dynamic-cast
4个回答
5
投票

使用void*指针唯一可以做的就是将它强制转换为与首先投射到void*的指针完全相同的类型。做其他事情的行为是未定义的。

在你的情况下你可以做的是定义

class Base
{
    public:
    virtual ~Base() = default; // make me a polymorphic type and make 
                               // polymorphic delete safe at the same time.
};

并使其成为AlphaBeta的基类。然后传递一个Base*指针而不是void*,并将你的dynamic_casts直接放在p上。

另请注意,如果您在virtual void Speak() = 0;中声明Base,那么您在main中的代码将变得简单

int main(){ 
    Base* p = new Alpha;
    p->Speak();
    delete p; // ToDo - have a look at std::unique_ptr
}

根据经验,任何类型的演员阵容都是不受欢迎的。


1
投票

表达式Alpha*a = dynamic_cast<Alpha*>((Alpha*)p);首先使用pAlpha*投射到explicit c style cast。然后,由此产生的Alpha*通过dynamic_cast<Alpha*>。在dynamic_cast<T*>指针上使用T*(与你试图转换为相同类型的指针)将始终提供输入指针。它不能用于确认指针是否有效。来自cppreferencedynamic_cast<new_type>(expression)

如果expression的类型恰好是new_type或qzxswpoi的cv限定版本,则结果是new_type的值,类型为expression

因此,代码将始终编译并运行,类型系统将无法保护您。但是由此产生的行为是不确定的。在new_type的情况下,你告诉编译器相信Beta*b = dynamic_cast<Beta*>((Beta*)p);是一个p,但事实并非如此。取消引用结果指针是未定义的行为,Beta*无法保护您免受此错误的影响。

如果您尝试删除显式类型转换,则会出现编译器错误。 dynamic_cast需要一个指针或对完整类型的引用,而dynamic_cast不是一个完整的类型。在使用void之前,您必须找到一种方法来跟踪指向您自己的实际类型并明确地将p转换为该指针类型。虽然在那时,如果你已经知道要投射的类型,它可能不再是必要的。

考虑使用常见的基本类型,或者如果需要可以使用dynamic_caststd::variant


1
投票

如果你使用C风格的转换来转换为std::any,类似于使用动态强制转换之前的static_cast,那么动态强制转换为无效。这里你的代码运行,因为这两个类具有相同的接口,但实际上这是未定义的行为。

通常,您希望使用动态强制转换从/向基类转发/向下传播到/从其派生类。

例如,如果我们添加一个基接口,然后将Alpha*指针转换为此基类,然后使用动态强制转换来尝试向上转换,代码按预期工作,只打印一次。

void *

产出:#include <stdio.h> class Speaker { public: virtual void Speak() = 0; }; class Alpha: public Speaker { public: virtual void Speak() { printf("A"); } }; class Beta: public Speaker { public: virtual void Speak() { printf("B"); } }; int main(){ void *p = new Alpha; // Convert to base type, using static_cast Speaker *s = static_cast<Speaker *>(p); // Attempt Upcasts Alpha*a = dynamic_cast<Alpha*>(s); Beta*b = dynamic_cast<Beta*>(s); // See if it worked if (a) a->Speak(); if (b) b->Speak(); return 0; }


0
投票

您必须知道从中转换void指针的类型。如果您不知道动态类型,则必须从指向base的指针创建void指针。如果您不知道创建void指针的指针类型,则无法使用void指针。

鉴于void指针是从A转换的,您可以使用静态转换将其转换回来:

Alpha*

然后,您可以使用auto a_ptr = static_cast<Alpha*>(p); 转换为派生类型。

dynamic_cast

如果动态类型不是if(auto d_ptr = dynamic_cast<DerivedAlpha*>(a_ptr)) { // do stuff with DerivedAlpha ,则动态转换将安全地返回null。您无法动态抛弃类型层次结构。由于DerivedAlphaAlpha与任何继承结构无关,因此它们不能相互动态转换。

© www.soinside.com 2019 - 2024. All rights reserved.