C++ хранит указатель на функцию-член неизвестного класса
Я хочу сохранить указатель на объект и указатель на его метод известной подписи. Если я знаю класс, то этот указатель имеет тип:
int (MyClass::*pt2Member)(float, char, char)
Но как я могу сохранить указатель, если я не знаю тип?
Я хочу сделать что-то вроде этого:
myObject.callThisFuncLater(&otherObject, &otherObject::method)
Как я могу сохранить указатель на метод method
в myObject
и позвонить позже?
4 ответа
Ты можешь использовать boost::function
(а также boost::bind
) хранить фрагмент кода для последующего вызова.
class MyClass
{
public:
void callThisFuncLater( boost::function< int (float, char, char) > callBack );
};
...
myObject.callThisFuncLater( boost::bind( &otherObject::method, &otherObject ) );
Самый простой способ сделать это, если у вас есть доступ к расширениям библиотеки TR1 STL (доступным в gcc и Visual Studio 2008 и далее, - это. Std:: function и std:: bind могут использоваться для переноса вызова, который может быть вызван позже. Эта функциональность также доступна в функции boost и boost bind:
#include <functional>
class MyClass {
public:
template<typename T> callThisFuncLater(T& otherObject,
int(T::*)(float, char, char) method) {
return storedInvocation_ = std::bind(otherObject,
method,
std::placeholders::_1, // float
std::placeholders::_2, // char
std::placeholders::_3); // char
}
int callStoredInvocation(float a, char b, char c) {
storedInvocation_(a, b, c);
}
private:
std::function<int(float, char, char)> storedInvocation_;
};
Нет простого способа сделать это так, как он изначально встроен в язык или стандартную библиотеку (хотя он был недавно добавлен). Если вы знакомы с Boost, они включают решение для этого - Boost.Function.
Однако если по какой-то причине вы не можете или не хотите использовать Boost, существует общий способ сделать это с помощью шаблонов (что, по общему признанию, довольно похоже на решение Boost):
class FncPtr
{
public:
virtual int call(float, char, char) = 0;
};
template <typename T>
class ClassFncPtr : public FncPtr
{
int (T::*pt2Member)(float, char, char);
T *inst;
public:
ClassFncPtr(T* who, int (T::*memfunc)(float,char,char))
: inst(who), pt2Member(memfunc)
{
}
int call(float a, char b, char c)
{
return (inst->*pt2Member)(a,b,c);
}
};
template <typename T>
FncPtr * makeFuncPointer(T* who, int (T::*memfunc)(float,char,char))
{
return new ClassFncPtr<T>(who,memfunc);
}
Вы также можете подкласс FncPtr
чтобы иметь возможность использовать не классовые функции, если хотите.
Лично я бы выбрал другой дизайн. Просто потому, что с указателями на функции-члены в C++ работать нелегко. Лично я бы предпочел использовать интерфейсы и наследовать от них и анализировать их.
Одна из проблем с указателями на функции-члены заключается в том, что они реализованы по-разному в разных компиляторах. Если вы используете компиляторы Borland/Embarcardero и хотите ограничиться этим, вы можете использовать __closure
ключевое слово, однако, скорее всего, это не так, и поэтому вам придется либо использовать какую-то другую специфическую для компилятора реализацию, либо использовать один из вспомогательных классов boost, таких как function.
Но если вы оказались в ситуации, когда вам полезно использовать указатели на функции-члены в C++, пересмотрите свой дизайн.