为什么要转换为空指针?

Why would you convert to a void pointer?

本文关键字:空指针 转换 为什么      更新时间:2023-10-16

>我在这个问题中看到以下代码:

class CFoo
{
int a;
public:
CFoo():a(1){}
~CFoo(){}
getNum(){return a;}
};
void tfunc(void* data)
{
CFoo* foo = static_cast<CFoo*>(data);
std::cout << "Number: " << foo->getNum();
delete foo;
}
int main()
{
CFoo* foo = new CFoo;
void* dt = static_cast<void*>(foo);
tfunc(dt); // or tfunc(static_cast<void*>(food));
return 0;
}

并开始思考为什么要将指向类型的指针转换为指向 void 的指针,而不是简单地将实际指针分配给 void 指针。就像在上面的代码中调用tfunc一样,他本可以像tfunc(&foo)一样调用它,而不是像使用静态强制转换static_cast<void*>(foo);那样将指针转换为 void 指针的类型,对吧?

你问题的措辞有点混乱,你实际上并不是在问为什么要转换为void*,你是在问为什么使用显式强制转换而不是依赖隐式强制转换,即

tfunc(static_cast<void*>(food)); // explicit casting

tfunc(food); // implicit casting

这个问题比投void*更普遍一些。不幸的是,C++允许相当数量的隐含危险铸件。例如,在singedunsigned之间,从较宽的整数或浮点到较窄的整数或浮点数,转换为布尔值和转换为void*。所有这些转换都有可能在代码中静默地引入错误。c++11在正确的方向上迈出了一小步,不允许在新的统一初始值设定项语法{}中缩小范围,但由于向后兼容性,仍然允许所有以前的隐式强制转换。

这就是鼓励显式转换的原因。它有助于显示作者的意图。它表明转换是明确需要的,而不是在作者不知情的情况下悄无声息地发生。同样非常重要的一点是,它有助于代码审查或阅读代码时,它被标记为"这里有投射。当您查看此代码时请注意这一点">

从上面的例子中,我不明白为什么函数tfunc的签名是void tfunc(void* data)而不是void tfunc(class CFoo* data)

无论如何,将任何类型 T 的指针转换为void *都是有效的,以后将这样的指针转换回T*是有效的。从T*转换可以是隐式的,而从void*转换回T*则需要显式转换。因此,在您的示例中,tfunc(foo)等效于tfunc(static_cast<void*>(foo))tfunc(dt)

关于"将CFoo*转换为void*"这一点,有一个标准转换(参见这个 C++ 在线标准草案):

4 (1) 标准转换是内置的隐式转换 意义。第4条列举了此类转换的全部集合。

4.10 指针转换 (2) "指向 cv T 的指针"类型的 prvalue,其中 T 是对象类型,可以转换为类型的 prvalue "指向简历无效的指针"。转换非空指针的结果 指向对象类型的指针的值表示 "指向 cv void 的指针" 内存中与原始指针值相同的字节的地址。 ...

我更喜欢隐式转换而不是显式转换,因为(隐式)标准转换的内置含义完全满足要求。

在函数签名中void*的一个用例可能是,人们想要传递不相关类型的对象,并在函数内部决定必须将其转换回哪种类型。例如,假设一个处理不同响应类型的回调的函数,并且每个响应类型可能传递不同的(类分层独立)对象:

void someCallback(int responseType, void *context) {
if(responseType == 1) {
CFoo1* foo1 = static_cast<CFoo1*>(context);
...
}
else {
CFoo2* foo2 = static_cast<CFoo2*>(context);
...
}
}