Могу ли я упростить это?
-
19-08-2019 - |
Вопрос
typedef void (FunctionSet::* Function)();
class MyFunctionSet : public FunctionSet
{
protected:
void addFunctions()
{
addFunction(Function(&MyFunctionSet::function1));
}
void function1()
{
// Do something.
}
};
Метод addFunction добавляет функцию в список базового класса.
которые затем можно перечислить для вызова всех функций.
Есть ли способ упростить (меньше печатать) добавление функций?
Решение
Похоже, вы назначаете указатель на функцию-член на функцию производного класса на указатель на функцию-член на функцию базового класса. Ну, это запрещено, потому что это открывает дыру в системе типов. Это неожиданно (по крайней мере, для меня, когда я впервые услышал это). Прочитайте этот ответ для Зачем. Р>
Чтобы ответить на ваш актуальный вопрос - я бы сделал addFunction
шаблон:
void addFunctions() {
addFunction(&MyFunctionSet::function1);
}
Измените static_cast
в базовом классе на этот:
template<typename Derived>
void addFunction(void(Derived::*f)()) {
myFunctions.push_back(static_cast<Function>(f));
}
Лучше использовать Derived
, потому что он сообщит вам, действительно ли FunctionSet
не является производным от <=>.
Другие советы
Можете ли вы объяснить, чего вы пытаетесь достичь этим?
Это выглядит как довольно плохой дизайн, разве вы не можете иметь абстрактный класс (" интерфейс c ++ "), такой как " Computable " который имеет чисто виртуальную функцию1, подкласс Computable для каждой реализации, а затем MyFunctionSet поддерживает набор Computable?
Есть ли конкретная причина, по которой вы используете указатели на функции?
Итак, я, как правило, не согласен с Ури.
Если вы реализуете шаблон Observer, вам нужно сказать следующее:
" когда объект X выполняет Y, я хочу выполнить код Z ".
Использование подхода, основанного исключительно на абстрактном классе, не лучший способ сделать это. Требование отдельного класса (особенно в C ++) для каждого обработчика событий является излишним. Если вы пришли с Java, вот как они все делают. Однако у Java есть 2 функции, которые делают это только немного раздражающим: анонимный класс и & Quot; instance & Quot; член классы. Это позволяет вам определить несколько обработчиков для нескольких событий в одном классе. Вы должны добавить к методам префикс & Quot; class {& Quot ;, но вы можете это сделать.
C ++ не имеет ни анонимных классов, ни " instance " член классы. Это делает использование абстрактного класса для событий гораздо более громоздким, если у вас есть несколько обработчиков, которым нужно совместно использовать состояние. Вы должны сделать то же самое, что и генерация замыканий вручную, что может довольно быстро раздражать.
.NET использует делегаты для обработки событий, которые в основном являются указателями на безопасные функции типов. Они делают код для обработки событий очень простым. В отличие от указателей на функции в C ++, делегат объединяет указатели на функции-члены и статические указатели на функции. По сути, это может привести к появлению & Quot; this & Quot; параметр любой функции-члена, оставляя указатели на функции, которые выглядят как статические указатели на функции. Это означает, что тип объекта, обрабатывающего событие, не является частью события & Quot; interface & Quot ;. Это делает его очень гибким.
Вы не можете сделать это напрямую, используя указатели на функции-члены C ++, потому что " this " Тип в конечном итоге является частью типа указателя на функцию, что ограничивает ваши обработчики только появлением в текущем классе.
Лучшее решение для C ++ - это гибрид между ними. Вам нужен универсальный интерфейс, подобный следующему:
class EventHandler
{
public:
virtual void Handle() = 0;
};
А затем реализация для функций-членов, подобных этой
template <class T>
class MemberFuncEventHandler : public EventHandler
{
public:
MemberFuncEventHandler(T * pThis, void (T::*pFunc)()) : m_pThis(pThis), m_pFunc(pFunc)
{
}
void Handle()
{
(m_pThis->*m_pFunc)();
}
private:
T* m_pThis;
void (T::*m_pFunc)();
};
template <class T>
EventHandler * Handler(T * pThis, void (T::*pFunc)())
{
return new MemberFuncEventHandler<T>(pThis, pFunc);
}
Вы также можете аналогичным образом определить класс обработчика для статических методов. Тогда вы можете просто делать такие вещи:
Handlers += Handler(obj, &(c1::foo));
Handlers += Handler(obj, &(c2::bar));
Handlers += Handler(blaa); // for static methods...
Полагаю, вы могли бы перегрузить оператор сложения.
Если вы пытаетесь создать систему обратного вызова или что-то в этом роде, я бы порекомендовал библиотеку Boost.Signals.По сути, он объединяет функции и вызывает группу функций по команде (как и любая другая библиотека сигналов), но он также предназначен для работы с Boost.Bind и Boost.Function, которые просто потрясающие.
Пример:
using boost::function
using boost::bind
using boost::signal
void some_other_func();
Foo a;
Bar b;
signal<void()> sig;
sig.connect(bind(&Foo::foo,&a));
sig.connect(bind(&Bar::bar,&b));
sig.connect(some_other_func);
sig(); // calls -> a.foo(), b.bar(), some_other_func()
Также поддерживает блокировку, сложное управление соединениями и другие полезные вещи.