关于void指针、类和类型转换

about void pointer, classes and casting

本文关键字:类型转换 void 指针 关于      更新时间:2023-10-16

我有大约一两年的c++经验,但我的编码方式与我在Java中的编码方式相同(简单的oop东西)。现在我有一个我不理解的示例代码。(它很大,所以我试着把它缩短,我希望它对你们来说足够清晰)

//in .h file
typedef void*(*AnimalCreation)();
//in .cpp
void foo(void* p)
{
AnimalCreation ac = (AnimalCreation)p;
Animal* current_animal = reinterpret_cast<Animal*>(ac());
current_animal->init();
}
//somewhere in another class foo is called
Dog* dog = new Dog(); //Dog is a subclass of Animal
foo((void*)&dog) 

动画创作的目的是什么?这和有什么区别

typedef void(*AnimalCreation)();`//without asterisk after void

foo内部发生了什么?

如果foo接收的预期参数总是Animal的子类,为什么程序员需要像上面那样实现它,而不仅仅是foo(Animal*)?

谢谢。

typedef void(*AnimalCreation)();

这声明"AnimalCreation"用作指向不返回任何值的函数的指针的类型别名,而这个

typedef void*(*AnimalCreation)();

声明它用作指向函数的指针的类型别名,该函数返回一个空指针,即指向您不知道其类型的对象的地址。

foo中,您收到这样一个"通用地址",并且C将其转换为函数指针(可能不安全,在运行时检查)。这是由你自己承担的风险:你不知道接收到的地址指向什么。然后你调用函数并接收另一个void指针,你将其重新解释为(危险的)Animal对象。然后你用它。

函数指针不能是任何东西的子类,所以我不认为代码中的参数是Animal子类。。。相反Animal类的子类是该函数返回的对象。假设它也是一个多态类,那么您将能够使用虚拟继承规则调用它的方法。如果您打算检查函数调用接收到的指针,并且不确定它是否是Animal类的子类,那么您宁愿使用dynamic_cast

附带说明:函数指针和void*之间的转换是C++中的一种错误做法,因为您会丢失有价值的类型信息。

typedef行是AnimalCreation,它被定义为函数指针类型

函数foo接收一个void*参数,并将其转换为AnimalCreation类型(即函数指针类型)。然后,它可以通过函数指针调用函数。此调用返回一个void*(根据typedef,第一个括号之前的部分是返回类型,因此是void*),然后通过repret_cast将其强制转换为Animal*。

如果从typdef中删除星号,它仍然会声明一个函数指针类型,但现在返回值将是void而不是void*(即不返回任何内容,而不是指针)。您仍然可以通过函数指针调用函数,但它不会返回任何内容。

总而言之,这是一个很好的函数指针教程。

EDIT:这段代码似乎在做什么——这是在C++中实现"工厂模式"的一种方法——抽象对象的创建,并返回一个指向派生类的多态基类指针。在void*和函数指针之间进行转换并重新解释_cast并不是实现这一点的最佳方式,您可以在这里查看

首先,这是一个非常丑陋的C风格代码。

typedef void*(*AnimalCreation)();

为了解释这一点,遵循C&C++声明读取:如果您将声明键入为表达式,您将获得它的类型。

  1. *AnimalCreation这意味着AnimalCreation是一个指针
  2. (*AnimalCreation)()这意味着*AnimalCreation是一个不带参数的函数,所以AnimalCreation是一个指向不带参数函数的指针
  3. void *(*AnimalCreation)()这意味着(*AnimalCreation)()void*(=指向void的指针),因此AnimalCreation是指向不带参数并返回void*的函数的指针

如果它只是typedef void (*AnimalCreation)();,那么它将是一个指向不带参数也不返回值的函数的指针(即返回void)。

现在,foo()

它获取一个void*(指向任何东西的指针),并将其解释为AnimalCreation——作为指向不带参数并返回void*的函数的指针。如果传递给foo的参数实际上是那种类型的,那么一切都很好。如果传入了其他内容,程序将显示未定义的行为,这意味着任何事情都可能发生。例如,它很可能会崩溃,因为它可能试图将数据解释为代码。

foo()调用传入的函数,返回一个void*。CCD_ 24随后将其解释为指向CCD_ 25的指针。如果这就是函数实际返回的结果,那就太好了。如果没有,则再次显示未定义的行为。

最后,您显示的调用将强制执行Undefined Behavior,因为它正在传递指向对象的指针的地址。但是,如上所述,foo()会将其解释为函数的地址,并尝试调用该函数。随之而来的是欢声笑语。

总之,这样的代码很糟糕,它的作者应该感到很糟糕。您唯一希望看到此类代码的地方是与C风格外部库的互操作性,在这种情况下,它应该有非常好的文档记录