Передать функцию-член класса в качестве параметра функции

у меня есть 2 вопроса класса 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++?

EDIT: реализация советов Спасибо за ответ, я попытался реализовать этот совет, его много, чтобы получить мою голову, хотя, это было бы правильно?

    // 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");
    }

2 ответов


статические функции-члены классов, в конечном счете, не отличается от обычной функции. Они действительно просто синтаксический сахар; функция просто имеет имя, которое включает Classname::.

нестатические члены-это совсем другое дело. Есть две важные вещи, которые следует помнить о нестатических функциях-членах (NSMF).

во-первых, каждая нестатическая функция-член имеет доступ к нестатическим членам класса, что они являются членами. Это возможно даже если у вас могут быть два объекта одного класса, которые хранят разные данные. Если у вас есть два std::string объекты, каждый из них хранит разные строки. Выполнение find в одной строке можно вернуть найденный результат в одном, но не в другом.

это потому, что каждый NSMF имеет неявное this указатель. this относится не только к классу, но и к фактическому объект на котором работает NSMF. Когда вы это сделаете:

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

на find функция принимает строковый аргумент, но также получает aString как его this. Каждый раз find ищет членов своего класса, он будет смотреть на 'ы.

Итак, давайте посмотрим на ваш предполагаемый вызов NSMF:

((*)nMemberFunction())

где объект, который он получает его this указатель? Без объекта NSMF не может получить доступ к нестатическим членам объекта, так как для него нет объекта, чтобы найти их. Это не законный.

Итак, правило №1 о NSMFs: ты должны вызовите их с фактическим экземпляром класса, членом которого является NSMF (или его производным классом). Вы не можете просто взять указатель NSMF и вызвать его как указатель функции; вы должны вызвать его на живом объекте этого типа.

Правило #2: синтаксис указателей NSMF действительно уродлив.

чтобы определить переменную (или аргумент) с именем arg типа указателя NSMF, вы делаете это:

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

здесь ReturnType является возвращаемым типом функции,ParameterList - это список аргументов, принятых функцией, и ClassName - это имя класса, к которому принадлежит указатель NSMF.

учитывая уродство, обычно лучше всего обернуть его в typedef:

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

таким образом, создавая typedef MemberPointerType, который является указателем NSMF.

данный объект с именем object типа ClassName, вы позвонили бы члену указатель arg следующим образом:

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

здесь Params аргументы, которые вы хотите передать. Если object указатель на ClassName вместо ссылки или значения вы используете .

еще одно: ты должны использовать & получить имя указателя. В отличие от указателей функций, указатели NSMF не преобразуются автоматически в указатели членов. Вы должны спросить их напрямую. Так что если ClassName имеет член, называемый функцией это соответствует выше ReturnType и ParameterList, ты заполнишь arg следующим образом:

arg = &ClassName::Function;

Правило №3: нестатические указатели членов не указатели. Да, они могут быть установлены в NULL (технически, они могут быть установлены в 0), но они не то же самое, что указатель.

большинство реальных компиляторов C и c++ позволят вам привести указатель функции к void* и обратно. Стандарты рассматривают это неопределенное поведение, но это не-совсем-неизвестно, чтобы сделать это. Ты абсолютно не может сделайте это с помощью указателя NSMF практически на всех компиляторах c++. Действительно,sizeof(MemberPointerType) скорее всего, не будет такого же размера, как void*.

таким образом, указатели NSMF не являются регулярными указателями. Не относитесь к ним как к таковым.


В C++ 11 они придумали способ сделать это. Читайте о функции и bind операции.

в вашем случае предположим, что вы хотите вызвать функции типа 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));