我的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之类的东西吗?这样安全吗?我希望有更好的解决方案,但我想不出别的。
使用void*
指针唯一可以做的就是将它强制转换为与首先投射到void*
的指针完全相同的类型。做其他事情的行为是未定义的。
在你的情况下你可以做的是定义
class Base
{
public:
virtual ~Base() = default; // make me a polymorphic type and make
// polymorphic delete safe at the same time.
};
并使其成为Alpha
和Beta
的基类。然后传递一个Base*
指针而不是void*
,并将你的dynamic_cast
s直接放在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
}
根据经验,任何类型的演员阵容都是不受欢迎的。
表达式Alpha*a = dynamic_cast<Alpha*>((Alpha*)p);
首先使用p
将Alpha*
投射到explicit c style cast。然后,由此产生的Alpha*
通过dynamic_cast<Alpha*>
。在dynamic_cast<T*>
指针上使用T*
(与你试图转换为相同类型的指针)将始终提供输入指针。它不能用于确认指针是否有效。来自cppreference的dynamic_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_cast
或std::variant
。
如果你使用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;
}
您必须知道从中转换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。您无法动态抛弃类型层次结构。由于DerivedAlpha
和Alpha
与任何继承结构无关,因此它们不能相互动态转换。