将类Member函数作为参数传递

Passing a class Member function as an Argument

本文关键字:参数传递 函数 Member 将类      更新时间:2023-10-16

我有一个如下所示的类和一个如下的主方法:

class Mesh
{
public:
    void draw()
    void draw(){}
}
int main(int argc,char* argv[])
{
    Mesh mesh;
    glutDisplayFunc(mesh.draw);
}

因此,我想传递一个对象的成员函数作为参数,但编译器显示以下错误消息:

error: argument of type ‘void (Mesh::)()’ does not match ‘void (*)()’

我做错了什么?

由于draw是一个非静态成员函数,它需要一个类的实例来操作。

在纯C++中,您将使用函数对象(也称为"函子",请参见std::function或其前身boost::function(。遗憾的是,这在C或C API中不是一个选项。

在这种情况下,由于没有数据被传递给回调,因此必须创建某种静态函数(类静态或文件静态(,以便在调用它时做正确的事情。

如果你只有一个Mesh,那么很容易:要么让类中的所有东西都是静态的(在这种情况下,它基本上是一个命名空间(,要么让一个非成员函数对你的Mesh:的一个实例调用draw

// at some global-ish file scope...
// we define our class
class Mesh { ... };
// and here's the one global instance of it
Mesh myOneMesh;
// and here is our global / static function that "knows"
// which instance to draw
void drawMyOneMesh() { myOneMesh.draw(); }
// then, in the main flow of your program:
glutDisplayFunc( &drawMyOneMesh );

如果有多个网格,则最好将其从当前窗口中设置关键帧。在不太了解你的应用程序或GLUT API的情况下,我可能会做这样的事情来启用每窗口网格回调:

#include <map>
// this could also be a class with all-static members, if one
// were so inclined.  might be worth it, if only to protect 'map'
// from external meddling.
namespace WindowToMesh
{
    typedef std::map< int, Mesh * > Map;
    Map map;
    void addMapping( int win, Mesh * mesh )
    {
        map.insert( { win, mesh } );
    }
    void removeMapping( int win )
    {
        map.erase( win );
    }
    void draw()
    {
        int win = glutGetWindow();
        Map::iterator it = map.find( win );
        if ( it != map.end() )
            it.second->draw();
    }
} // end namespace WindowToMesh

现在,在您的主程序中,您可以将新的Mesh与当前窗口关联起来:

Mesh * m = new Mesh( ... );
WindowToMesh::addMapping( glutGetWindow(), m );

您可以将(实际上是静态的(WindowToMesh::draw函数关联为回调:

glutDisplayFunc( &WindowToMesh::draw );

当你准备销毁Mesh时,确保你在同一个窗口中,然后:

WindowToMesh::removeMapping( glutGetWindow() );

根据其他因素,进行双向映射可能是有意义的(这样你就可以通过Mesh *而不仅仅是窗口int找到东西,或者对罕见的注销进行强力扫描,等等

我没有一个可以测试的环境,但应该很接近。祝你好运

问题是,成员函数指针需要一些信息才能被调用,即要调用的实例,然后this指针可以在成员函数的实现中访问这些信息,因此您可以访问成员变量。我认为你想这样做,因为你的设计看起来像是你想画Mesh中定义的东西。

有可能将成员函数指针转换为"正常"函数指针。但您必须记住,一旦调用此函数,这条信息(Mesh实例(就必须可用。这意味着从成员函数指针到"正常"函数指针的典型转换会添加参数(实例(。

但是glutDisplayFunc()接受void(*)(void)函数指针,即空的参数列表,因此它不会告诉被调用函数任何。您必须确保一旦调用了传递的函数,就可以以任何方式重建"上下文",即Mesh实例。

这可以通过从代码中的全局点指向Mesh实例来实现。当您调用我们即将编写的助手函数时,它将始终被使用。因此,当您将指针传递给glutDisplayFunc()时,一旦我们告诉它应该使用哪个Mesh,我们的助手函数就会知道该使用哪个。

class Mesh
{
public:
    void draw() { ... }
}
Mesh *instance;
// Helper function called by glut:
void display() {
    // Now we can access a particular instance:
    instance->draw();
}
int main(int argc,char* argv[])
{
    Mesh mesh;
    // First point to this instance globally:
    instance = &mesh;
    // Then, pass our helper function which now has an empty parameter list:
    glutDisplayFunc(&display);
}

此解决方案有一个限制。不能在不同的实例中重复使用同一个函数指针。因此,不能将任何实例转换为函数指针,而只能为每个全局定义的实例指针转换一个。但由于您只想将draw函数传递给glutDisplayFunc,因此只需要执行一次,因此应该可以。我只是不想对你隐瞒这种"危险"。(