Передайте функцию-член класса в качестве параметра функции - PullRequest
7 голосов
/ 28 августа 2011

У меня есть 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 ++?

РЕДАКТИРОВАТЬ: Выполнение рекомендаций Спасибо за ответ, я попытался реализовать этот совет, хотя его много, чтобы разобраться в этом,это будет правильно?

    // 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("\r\n");
    }

    void catalogueStaticTest( tstring testName, bool DebuggingManager::nStaticFunction )
    {
        debugLog += _T("Status of ") + testName + _T(": ") + nStaticFunction + _T("\r\n");
    }

Ответы [ 2 ]

13 голосов
/ 28 августа 2011

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

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

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

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

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

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

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

((*)nMemberFunction())

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

Итак, правило № 1 о NSMF: вы должны вызывать их с фактическим экземпляром класса, членом которого является 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 вместо ссылки или значения, тогда вы используете object->*arg вместо.

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

arg = &ClassName::Function;

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

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

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

2 голосов
/ 09 июля 2015

В 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));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...