2
typedef void (FunctionSet::* Function)();

class MyFunctionSet : public FunctionSet
{
protected:
    void addFunctions()
    {
        addFunction(Function(&MyFunctionSet::function1));
    }

    void function1()
    {
        // Do something.
    }
};

addFunction 方法将函数添加到基类中的列表中,
然后可以枚举该列表以调用所有函数。

有什么方法可以简化(减少打字工作)添加功能?

4

5 回答 5

2

你能解释一下你想通过这样做来实现什么吗?

这似乎是一个相当糟糕的设计,难道你不能有一个抽象类(“c++ 接口”),例如“Computable”,它有一个纯虚函数1,每个实现的子类 Computable,然后让 MyFunctionSet 维护一组 Computable?

您使用函数指针是否有特定原因?

于 2009-01-19T23:52:01.133 回答
2

看起来您将一个指向派生类函数的成员函数指针分配给一个指向基类函数的成员函数指针。好吧,这是被禁止的,因为它在类型系统中打开了一个漏洞。这是一个惊喜(至少对我来说,这是我第一次听说)。阅读此答案以了解原因。

要回答您的实际问题 - 我会制作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.

于 2009-01-20T00:24:06.840 回答
2

所以,我倾向于在某种程度上不同意 Uri。

如果你正在实现观察者模式,你想要的是能够说:

“当对象 X 执行 Y 时,我想执行代码 Z”。

采用纯粹基于抽象类的方法并不是最好的方法。为每个事件处理程序要求一个单独的类(尤其是在 C++ 中)是多余的。如果您来自 Java,那么他们就是这样做的。然而,Java 有 2 个特性使这有点烦人:匿名类和“实例”成员类。这允许您为同一类中的多个事件定义多个处理程序。您必须在方法前面加上“class {”,但您可以这样做。

C++ 没有匿名类或“实例”成员类。如果您有多个需要共享状态的处理程序,这使得为事件使用抽象类变得更加麻烦。您必须手动执行相当于生成闭包的操作,这很快就会变得非常烦人。

.NET 使用委托进行事件处理,这基本上是类型安全的函数指针。它们使处理事件的代码非常直接。然而,与 C++ 中的函数指针不同,委托统一了成员函数指针和静态函数指针。基本上它可以curry任何成员函数的“this”参数,留下一个看起来就像一个静态函数指针的函数指针。这意味着处理事件的对象类型不是事件“接口”的一部分。这使得它非常灵活。

您不能直接使用 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...
于 2009-01-20T00:53:05.517 回答
1

我想你可以重载加法运算符。

于 2009-01-19T23:48:55.023 回答
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()

还支持阻塞、复杂的连接管理和其他简洁的东西。

于 2009-01-20T01:04:39.863 回答