Передать функцию-член класса в качестве параметра функции
у меня есть 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));