代码之家  ›  专栏  ›  技术社区  ›  user3364552

关于void指针、类和转换

  •  3
  • user3364552  · 技术社区  · 12 年前

    我有大约一两年的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) 
    

    AnimalCreation的目的是什么? 这和

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

    foo内部发生了什么?

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

    谢谢

    3 回复  |  直到 12 年前
        1
  •  2
  •   Marco A.    12 年前
    typedef void(*AnimalCreation)();
    

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

    typedef void*(*AnimalCreation)();
    

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

    在…内 foo 你收到了这样一个“通用地址” C型铸造 (可能不安全,在运行时检查)它指向函数指针。这是由你自己承担的风险:你不知道收到的地址指向什么。然后,你调用函数并收到另一个void指针,你将其重新解释为 Animal 对象然后你使用它。

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

    附带说明:函数指针和void*之间的转换是 不良行为 因为您丢失了有价值的类型信息。

        2
  •  2
  •   Community Mohan Dere    9 年前

    typedef行是AnimalCreation,定义为 function pointer type

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

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

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

    编辑:这段代码似乎在做什么——这是在C++中实现“工厂模式”的一种方式——抽象对象的创建,并将多态基类指针返回到派生类。在void*和函数指针之间进行强制转换,并不是实现这一点的最佳方法,您可以寻找替代方法 here

        3
  •  0
  •   Angew is no longer proud of SO    12 年前

    首先,这是一个相当丑陋的C样式代码。

    typedef void*(*AnimalCreation)();
    

    要解释这一点,请遵循C&C++声明读取:如果将声明作为表达式键入,则将获得其类型。

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

    如果只是 typedef void (*AnimalCreation)(); ,它将是一个指向函数的指针,该函数不接受参数,也不返回值(即返回 无效的 ).

    现在 foo() .

    这需要一个 无效* (指向任何对象)并将其解释为 动画创作 -作为指向函数的指针,不接受参数并返回 无效* 。如果参数传递给 foo 其实是那种类型的,一切都很好。如果传递了其他内容,程序将显示 未定义行为, 这意味着任何事情都可能发生。它很可能会崩溃,例如,它可能试图将数据解释为代码。

    foo() 调用传入的函数,该函数返回 无效* . foo() 然后将其解释为指向 Animal 。如果这是函数实际返回的,那就太好了。如果没有,则再次显示未定义行为。

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

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