将类成员函数作为函数参数传递

Pass a class member function as a function parameter

本文关键字:函数 参数传递 成员      更新时间:2023-10-16

我有两个C++类问题:

第一个问题是:我如何才能将类成员函数作为参数传递给另一个函数&然后我如何运行/调用该函数?以及如何对类静态函数执行同样的操作。通过查看以下代码可能更容易理解我的问题:

class DebuggingManager
{
    string testLog;
    bool test1()
    {
         // run test & return whether it passed or failed
    }    
    static bool test2()
    {
    }
    // How can I call a member function?
    void catalogueTest( string testName, bool DebuggingManager::*nMemberFunction )
    {
        testLog += "Status of " + testName + ": " + ((*)nMemberFunction()) + "n"; 
    }
    // How can I call a static function?
    void catalogueTest( string testName, bool DebuggingManager::*nStaticFunction )
    {
        testLog += "Status of " + testName + ": " + DebuggingManager::nStaticFunction() + "n"; 
    }
    // how do I pass a member function or a static function as a parameter in another function 
    bool runTests()
    {
         catalogueTest( "Test of member functin", test1() );
         catalogueTest( "Test of static functin", test2() );
    }
};

第二个问题是:像上面那样间接调用类成员(或静态(函数是坏的(或危险的(做法吗。我有一种感觉,这真的是糟糕的C++练习吗?

编辑:实施建议谢谢你的回复,我已经尝试过执行这个建议,不过这很难让我清醒过来,这是正确的吗?

    // I have a feeling that ParameterList is incorect, would I pass the implicit obj as a parameter or is it done automatically like in normal object function calls?
    typedef bool (DebuggingManager::*MemberPointerType)(ParameterList); 
    void catalogueTest( tstring testName, DebuggingManager* obj, MemberPointerType *nMemberFunction )
    {
        debugLog += _T("Status of ") + testName + _T(": ") + (obj->*nMemberFunction)() + _T("rn");
    }
    void catalogueStaticTest( tstring testName, bool DebuggingManager::nStaticFunction )
    {
        debugLog += _T("Status of ") + testName + _T(": ") + nStaticFunction + _T("rn");
    }

类的静态成员函数最终与正则函数没有什么不同。它们实际上只是句法上的糖;函数只是具有包括CCD_ 1的名称。

非静态成员完全是另一回事。关于非静态成员函数(NSMF(,有两件重要的事情需要记住。

首先,每个非静态成员函数都可以访问它们所属类的非静态成员。即使同一类的两个对象碰巧存储了不同的数据,这也是可能的。如果您有两个std::string对象,它们各自存储不同的字符串。对一个字符串执行find可以在一个字符串中返回已找到的结果,但在另一个字符串上则不能返回。

这是因为每个NSFF都有一个隐含的this指针。this不仅指一个类,而且指NSMF操作的实际对象。当你这样做时:

std::string aString("data");
aString.find("da");

find函数接受一个字符串参数,但它也将aString作为其this。每次find查找其类的成员时,它都会查看Classname::0的数据。

因此,让我们来看看您对NSMF:的预期调用

((*)nMemberFunction())

this指针来自哪个对象?如果没有对象,NSMF就无法访问对象的非静态成员,因为它没有对象可以在其中找到它们。这是不合法的。

因此,关于NSMF的规则#1:必须使用NSMF所属类(或其派生类(的实际实例来调用它们。您不能只获取一个NSFF指针并像函数指针一样调用它;你必须在这种类型的活动对象上调用它。

规则#2:NSMF指针的语法非常难看。

要定义NSMF指针类型的名为arg的变量(或自变量(,请执行以下操作:

ReturnType (ClassName::*arg)(ParameterList);

其中,ReturnType是函数的返回类型,ParameterList是函数采用的参数列表,ClassName是NSFF指针所属类的名称。

考虑到丑陋,通常最好将其封装在typedef:中

typedef ReturnType (ClassName::*MemberPointerType)(ParameterList);

从而创建typedef MemberPointerType,它是一个NSMF指针。

给定一个名为object、类型为ClassName的对象,您将调用成员指针arg,如下所示:

ReturnType value = (object.*arg)(Params);

其中Params是要传递的参数。如果object是指向ClassName的指针,而不是引用或值,则使用object->*arg

还有一件事:必须使用&来获取成员指针名称。与函数指针不同,NSFF指针不会自动转换为成员指针。你必须直接要求他们。因此,如果ClassName有一个名为Function的成员,它符合上述ReturnTypeParameterList,那么您将按如下方式填充arg

arg = &ClassName::Function;

规则#3:非静态成员指针是而不是指针。是的,它们可以被设置为NULL(从技术上讲,它们可以设置为0(,但它们是而不是与指针相同。

大多数真正的C和C++编译器都允许您将函数指针投射到void*并返回。标准考虑了这种未定义的行为,但这样做并非完全未知。在几乎所有的C++编译器上,使用NSMF指针是绝对不能做到这一点的。实际上,sizeof(MemberPointerType)的大小可能与void*的大小不同。

因此,NSMF指针不是常规指针。不要这样对待他们。

在C++11中,他们想出了一种方法来做到这一点。阅读有关函数和绑定操作的信息。

在您的情况下,假设您想要调用类型为test1的函数。(即形式为bool FunctionName((。

void catalogueTest( string testName, std::function<bool()> myFunction)
{
    testLog += "Status of " + testName + ": " + myFunction() + "n"; 
}

这样称呼它:

DebuggingManager myInstance
myInstance->catalogueTest("TestName", std::bind(&DebuggingManager::test1, myInstance));

这是您想要的,其中X是您的类,T是您的方法的返回,Args是您的函数接收的参数。

如果您的方法不接受args,请不要像test1和test2那样传递它们。

如果您的方法有void返回,则擦除T模板并在方法签名上的T处写入void。

如果您有链接错误,请记住在.h文件中定义doMember和doStatic。我所说的define指的是编写整个函数,而不仅仅是它的签名。这是由于模板问题。

#include <string>
class DebuggingManager
{
private:
    std::string testLog;
    bool test1()
    {
        // run test & return whether it passed or failed
        return true;
    }
    static bool test2()
    {
        return false;
    }
    int test3(int a, int b)
    {
        return a + b;
    }
    static int test4(int a, int b)
    {
        return a - b;
    }
    void catalogueTest(std::string testName, int result)
    {
        testLog += "Status of " + testName + ": " + std::to_string(result) + "n";
    }
    // How can I call a member function?
    template <typename X, typename T, typename ...Args>
    T doMember(X* obj, T(X::* func)(Args...), Args... args)
    {
        return (obj->*func)(args...);
    }
    // How can I call a static function?
    template <typename T, typename ...Args>
    T doStatic(T func(Args...), Args... args)
    {
        return func(args...);
    }
public:
    // how do I pass a member function or a static function as a parameter in another function 
    void runTests()
    {
        catalogueTest("Test of member functin", doMember(this, &DebuggingManager::test1));
        catalogueTest("Test of member functin", doMember(this, &DebuggingManager::test3, 10, 20));
        catalogueTest("Test of static functin", doStatic(DebuggingManager::test2));
        catalogueTest("Test of static functin", doStatic(DebuggingManager::test4, 10, 20));
        std::cout << testLog << std::endl;
    }
};