使用类型擦除:
class BarBase {
virtual void invoke() = 0;
};
template<typename TypeName>
class Bar final : public BarBase {
void(TypeName::*action)();
TypeName* objPtr;
virtual void invoke() override { (objPtr->*action)(); }
};
class Foo {
std::vector<std::unique_ptr<BarBase>> myBars;
/* ... */
};
template <typename TypeName>
void Foo::foo ( void(TypeName::*action)() , TypeName* objPtr)
{
auto bar = new Bar<TypeName>();
bar->action = action;
bar->objPtr = objPtr;
myBars.emplace_back(bar);
};
然后只需调用myBars[x]->invoke();
回答评论中的问题。
怎么了override
?明确地说您从基类中重写虚函数。这在这里并不是真正必要的,但被认为是良好的做法。有关更多详细信息,请参阅Wikipedia 文章。
它是 C++11 的一个新特性,一些编译器在很长一段时间内都以某种方式支持这个特性,但它直到现在才被标准化。GCC 应该从 4.7 版本开始支持这个特性——你必须使用命令行参数-std=c++0x
。
怎么了unique_ptr
?它使生活更轻松。当您分配时new Bar<XXX>()
,您必须在某个时候说delete
释放该内存。如果您将指针放在像 之类的资源管理对象unique_ptr
中,则不再需要担心删除它。你将vector<BarBase*>
不得不声明一个析构函数,Foo
它执行以下操作:
for each element in myBars
delete element
如果您使用vector<unique_ptr<BarBase>>
,则无需担心删除任何内容。当在 的生命vector
结束时被销毁时Foo
,它将销毁其元素 - 并unique_ptr
删除它包含在自己的析构函数中的内存。
这也是 C++11 的特性——标准库的补充。您不必使用它(但请确保最后删除所有内容)。
怎么了auto
?而不是重复相同的类型两次(Bar<Type> * bar = new Bar<Type>();
),只需使用一次,然后让编译器根据初始化程序的类型推断出变量的正确类型。它的行为完全相同,只是打字更少,看起来更好:)
还有 C++11 特性,自 v4.4 起在 GCC 中得到支持。
为什么myBars[x]->invoke()
做对的事?因为invoke
在BarBase
. 这意味着执行的方法是根据 的动态类型(执行期间的实际类型)而myBars[x]
不是静态类型来选择的。有关深入解释,请参阅Wiki。这种机制有一点运行时开销,但在处理动态类型时无济于事。