typedef void (FunctionSet::* Function)();
class MyFunctionSet : public FunctionSet
{
protected:
void addFunctions()
{
addFunction(Function(&MyFunctionSet::function1));
}
void function1()
{
// Do something.
}
};
addFunction方法将函数添加到基类中的列表中 然后可以枚举它来调用所有函数。
有没有办法简化(减少打字工作)添加功能?
答案 0 :(得分:2)
你可以通过这样做来解释你想要达到的目标吗?
这似乎是一个相当糟糕的设计,你不能拥有一个抽象类(“c ++接口”),如“Computable”,它有一个纯虚函数1,每个实现的子类Computable,然后有MyFunctionSet维护一个集合可计算?
您是否有使用函数指针的具体原因?
答案 1 :(得分:2)
看起来您将派生类的函数的成员函数指针指定给指向基类函数的成员函数指针。嗯,这是禁止的,因为它在类型系统中打开了一个洞。令人惊讶的是(至少对我来说,这是我第一次听到这个)。请阅读this answer了解原因。
要回答您的实际问题 - 我会将addFunction
作为模板:
void addFunctions() {
addFunction(&MyFunctionSet::function1);
}
将基类中的addFunction
更改为:
template<typename Derived>
void addFunction(void(Derived::*f)()) {
myFunctions.push_back(static_cast<Function>(f));
}
更好地使用static_cast
,因为它会告诉您Derived
实际上是否来自FunctionSet
。
答案 2 :(得分:2)
所以,我倾向于不同意Uri。
如果您正在实施观察者模式,您想要的是:
“当对象X执行Y时,我想执行代码Z”。
采用纯粹基于抽象类的方法并不是最好的方法。为每个事件处理程序要求一个单独的类(特别是在C ++中)是过度的。如果你来自Java,他们就是这样做的。但是,Java有2个功能,这使得这只有点烦人:匿名类和“实例”成员类。这允许您为同一个类中的多个事件定义多个处理程序。你必须在方法前加上“class {”,但你可以这样做。
C ++没有匿名类或“实例”成员类。如果您有多个需要共享状态的处理程序,这会使事件的抽象类更加麻烦。你必须手工完成相当于闭包的生成,这很快就会变得很烦人。
.NET使用委托进行事件处理,它们基本上是类型安全的函数指针。他们使处理事件的代码非常直接。但是,与C ++中的函数指针不同,委托统一了成员函数指针和静态函数指针。基本上它可以调用任何成员函数的“this”参数,留下一个看起来就像静态函数指针的函数指针。这意味着处理事件的对象的类型不是事件“interface”的一部分。这使它非常灵活。
您无法直接使用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...
答案 3 :(得分:1)
我想你可以重载加法运算符。
答案 4 :(得分:1)
如果您正在尝试制作回调系统,我会推荐使用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()
还支持阻止,复杂的连接管理和其他整洁的东西。