人们通常使用以下几种模式之一:
遗产。也就是说,您定义了一个包含回调的抽象类。然后你拿一个指针/引用它。这意味着任何人都可以继承并提供此回调。
class Foo {
virtual void MyCallback(...) = 0;
virtual ~Foo();
};
class Base {
std::auto_ptr<Foo> ptr;
void something(...) {
ptr->MyCallback(...);
}
Base& SetCallback(Foo* newfoo) { ptr = newfoo; return *this; }
Foo* GetCallback() { return ptr; }
};
再次继承。也就是说,您的根类是抽象的,用户从它继承并定义回调,而不是拥有一个具体的类和专用的回调对象。
class Foo {
virtual void MyCallback(...) = 0;
...
};
class RealFoo : Foo {
virtual void MyCallback(...) { ... }
};
更多的继承 - 静态的。这样,您可以使用模板来更改对象的行为。它类似于第二个选项,但在编译时而不是在运行时工作,这可能会产生各种好处和缺点,具体取决于上下文。
template<typename T> class Foo {
void MyCallback(...) {
T::MyCallback(...);
}
};
class RealFoo : Foo<RealFoo> {
void MyCallback(...) {
...
}
};
您可以获取和使用成员函数指针或常规函数指针
class Foo {
void (*callback)(...);
void something(...) { callback(...); }
Foo& SetCallback( void(*newcallback)(...) ) { callback = newcallback; return *this; }
void (*)(...) GetCallback() { return callback; }
};
有函数对象——它们重载了operator()。您将想要使用或编写一个函数式包装器——目前在 std::/boost:: 函数中提供,但我也会在这里演示一个简单的包装器。它类似于第一个概念,但隐藏了实现并接受了大量其他解决方案。我个人通常使用它作为我选择的回调方法。
class Foo {
virtual ... Call(...) = 0;
virtual ~Foo();
};
class Base {
std::auto_ptr<Foo> callback;
template<typename T> Base& SetCallback(T t) {
struct NewFoo : Foo {
T t;
NewFoo(T newt) : t(newt) {}
... Call(...) { return t(...); }
};
callback = new NewFoo<T>(t);
return this;
}
Foo* GetCallback() { return callback; }
void dosomething() { callback->Call(...); }
};
正确的解决方案主要取决于上下文。如果您需要公开 C 风格的 API,那么函数指针是唯一的方法(请记住 void* 用于用户参数)。如果您需要在运行时进行更改(例如,在预编译库中公开代码),则此处不能使用静态继承。
快速说明:我手动编写了该代码,因此它并不完美(例如函数的访问修饰符等),并且可能存在一些错误。这是一个示例。