C ++成员函数调用

时间:2013-03-01 00:27:21

标签: c++ class methods member

好的,我经常看到使用以下类型的事件处理:

Connect(objectToUse, MyClass::MyMemberFunction);

用于某种事件处理,其中objectToUse的类型为MyClass。我的问题是这是如何工作的。你会如何将它转换成objectToUse->MyMemberFunction()

的东西

MyClass::MyMemberFunction是否从类的开头给出一个偏移量,然后可以用作函数指针?

4 个答案:

答案 0 :(得分:2)

通常,这使用static成员函数(将指针作为参数),在这种情况下,objectToUse作为参数传入,MyMemberFunction将使用objectToUse设置指向MyClass对象的指针,并使用它来引用成员变量和成员函数。

在这种情况下,Connect将包含以下内容:

void Connect(void *objectToUse, void (*f)(void *obj))
{
     ...
     f(objectToUse);  
     ...
}

[很有可能fobjectToUse被保存在以后使用的地方,而不是实际在Connnect内部,但在这种情况下调用看​​起来也一样 - 只是从一些其他函数被调用为该函数应该被调用的事件的结果。

使用指向成员函数的指针也是可能的,但它非常复杂,并且根本不容易“正确” - 无论是语法还是“何时以及如何正确使用它”。查看更多here

在这种情况下,Connect看起来有点像这样:

void Connect(MyClass *objectToUse, void (Myclass::*f)())
{
     ...
     objectToUse->*f();
     ...
}

很可能使用模板,就好像Connect类中已知“MyClass”一样,拥有一个函数指针是没有意义的。虚拟功能将是一个更好的选择。

在适当的情况下,您也可以使用虚函数作为成员函数指针,但它需要编译器/环境“一起玩”。以下是关于该主题的更多细节[我没有任何个人经验:指向虚拟成员函数的指针。它是如何工作的?

Vlad还指出Functors,它是一个包装函数的对象,允许具有特定行为的对象作为“函数对象”传递。通常,这涉及预定义的成员函数或operatorXX,它被称为函数中处理的一部分,需要回调到代码中。

C ++ 11允许“Lambda函数”,它是代码中即时声明的函数,没有名称。这是我根本没用过的东西,所以我不能对此进行进一步的评论 - 我已经读过它了,但没有必要在我的(爱好)编程中使用它 - 我的大部分工作时间都是使用C而不是C ++,尽管我已经使用C ++工作了5年。

答案 1 :(得分:2)

除了Mats的回答之外,我还将举例说明如何在这类事物中使用非静态成员函数。如果您不熟悉指向成员函数的指针,可能需要先查看FAQ

然后,考虑这个(相当简单)的例子:

class MyClass
{
public:
    int Mult(int x)
    {
        return (x * x);
    }

    int Add(int x)
    {
        return (x + x);
    }
};

int Invoke(MyClass *obj, int (MyClass::*f)(int), int x)
{ // invokes a member function of MyClass that accepts an int and returns an int
  // on the object 'obj' and returns.
    return obj->*f(x);
}

int main(int, char **)
{
    MyClass x; 

    int nine = Invoke(&x, MyClass::Mult, 3);
    int six = Invoke(&x, MyClass::Add, 3);

    std::cout << "nine = " << nine << std::endl;
    std::cout << "six = " << six << std::endl;

    return 0;
}

答案 2 :(得分:1)

我可能在这里错了,但据我了解,

在C ++中,具有相同签名的函数是相同的。

具有n个参数的C ++成员函数实际上是具有n + 1个参数的普通函数。换句话说,void MyClass::Method( int i )生效void (some type)function( MyClass *ptr, int i)

因此,我认为Connect在幕后工作的方式是将成员方法签名转换为普通的函数签名。它还需要一个指向实例的指针来实际连接工作,这就是为什么它需要objectToUse

换句话说,它本质上是使用指向函数的指针并将它们转换为更通用的类型,直到可以使用提供的参数和附加参数调用它,该附加参数是指向对象实例的指针

如果方法是静态的,那么指向实例的指针没有意义,它是直接类型转换。我还没有弄清楚非静态方法所涉及的复杂性 - 看看boost::bind的内部结构可能是你想要理解的:)这是静态函数的工作原理。

#include <iostream>
#include <string>

void sayhi( std::string const& str )
{
    std::cout<<"function says hi "<<str<<"\n";
}

struct A
{
    static void sayhi( std::string const& str )
    {
        std::cout<<"A says hi "<<str<<"\n";
    }
};

int main()
{
    typedef void (*funptr)(std::string const&);
    funptr hello = sayhi;
    hello("you"); //function says...
    hello = (&A::sayhi); //This is how Connect would work with a static method
    hello("you"); //A says...
    return 0;
}

答案 3 :(得分:0)

对于事件处理或回调,它们通常采用两个参数 - 回调函数和userdata参数。回调函数的签名将userdata作为参数之一。

调用事件或回调的代码将直接使用userdata参数调用该函数。像这样的东西:

eventCallbackFunction(userData);

在您的事件处理或回调函数中,您可以选择使用userdata执行您想要的任何操作。

由于函数需要在没有对象的情况下直接调用,因此它可以是全局函数或类的静态方法(不需要对象指针)。

静态方法有一些限制,它只能访问静态成员变量并调用其他静态方法(因为它没有 this 指针)。这就是userData可用于获取对象指针的地方。

考虑到这一点,请查看以下示例代码段:

class MyClass
{
...
public:
    static MyStaticMethod(void* userData)
    {
        // You can access only static members here
        MyClass* myObj = (MyClass*)userdata;
        myObj->MyMemberMethod();
    }

    void MyMemberMethod()
    {
        // Access any non-static members here as well
        ...
    }

...
...
};

MyClass myObject;
Connect(myObject, MyClass::MyStaticMethod);

正如您所看到的,您可以访问成员变量和方法作为事件处理的一部分,如果您可以创建一个静态方法,该方法将首先使用对象指针(从userData检索)将调用链接到成员方法)。