我有一种数据类型必须存储在一个连续的数组中,为了更新这些数据,迭代的数组被迭代.棘手的部分是我希望有可能动态地改变任何对象的更新方式.
这是我到目前为止所提出的:
struct Update {
virtual void operator()(Data & data) {}
};
struct Data {
int a, b, c;
Update * update;
};
struct SpecialBehavior : public Update {
void operator()(Data & data) override { ... }
};
Run Code Online (Sandbox Code Playgroud)
然后我会为每个数据对象分配一些类型的Update.然后在更新期间,所有数据都会传递给自己的更新仿函数:
for (Data & data : all)
data->update(data);
Run Code Online (Sandbox Code Playgroud)
据我所知,这就是战略模式.
我的问题:有没有办法更有效地做到这一点?某些方法可以实现相同的灵活性而无需调用虚拟方法的成本?
cma*_*ter 16
虚函数调用的开销是多少?那么,实现必须做两件事:
这恰恰是两个记忆的间接.您可以通过将函数指针直接放在对象中来避免两者中的一个(避免从对象中查找vtable指针),这是ralismarks answer给出的方法.
这有一个缺点,它只适用于单个虚函数,如果你添加更多,你将使用函数指针膨胀你的对象,导致你的缓存压力更大,因此可能会降低性能.只要你只是替换一个虚函数,那就没关系,再添加三个,你的对象膨胀了24个字节.
除非确保编译器可以Update在编译时派生实际类型,否则无法避免第二个内存间接.而且由于似乎是使用虚函数在运行时执行决策的重点,所以你运气不好:任何"删除"间接的尝试都会产生更糟糕的性能.
(我说"删除"加上引号,因为你肯定能避免查找从内存中的函数指针,价格将是您要执行类似一个switch()或else if()某些类型的标识值梯子从对象加载,这将变成是比仅仅从对象加载函数指针更昂贵.ralismarks的第二个解决方案明确地做了这个,而Vittorio Romeo的std::variant<>方法将它隐藏在模板中.间接并没有真正被删除,它只是隐藏在更慢的操作中.)std::variant<>
ral*_*ark 10
您可以使用函数指针代替.
struct Data;
using Update = void (*)(Data &);
void DefaultUpdate(Data & data) {};
struct Data {
int a, b, c;
Update update = DefaultUpdate;
};
void SpecialBehavior(Data & data) { ... };
// ...
Data a;
a.update = &SpecialBehaviour;
Run Code Online (Sandbox Code Playgroud)
这避免了虚函数的成本,但仍然具有使用函数指针(较少)的成本.从C++ 11开始,您还可以使用非捕获lambdas(可以隐式转换为函数指针).
a.update = [](Data & data) { ... };
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用enum和switch语句.
enum class UpdateType {
Default,
Special
};
struct Data {
int a, b, c;
UpdateType behavior;
};
void Update(Data & data) {
switch(data.behavior) {
case UpdateType::Default:
DoThis(data);
break;
case UpdateType::Special:
DoThat(data);
break;
}
}
Run Code Online (Sandbox Code Playgroud)
如果你不需要开集多态 (即你事先知道所有可以派生的类型Update),你可以使用像或的变体:std::variantboost::variant
struct Update0 { void operator()(Data & data) { /* ... */ } };
struct Update1 { void operator()(Data & data) { /* ... */ } };
struct Update2 { void operator()(Data & data) { /* ... */ } };
Run Code Online (Sandbox Code Playgroud)
struct Data {
int a, b, c;
std::variant<Update0, Update1, Update2> update;
};
Run Code Online (Sandbox Code Playgroud)
for (Data & data : all)
{
std::visit(data.update, [&data](auto& x){ x(data); });
}
Run Code Online (Sandbox Code Playgroud)
这将允许您:
避免virtual函数调用的成本.
Data以缓存友好的方式存储您的实例.
具有Update不同接口或任意不同状态的类.
另外,如果你想允许open-set多态但只允许通过operator()(Data&)接口,你可以使用类似的东西function_view,它基本上是对具有特定签名的函数对象的类型安全引用.
struct Data {
int a, b, c;
function_view<void(Data&)> update_function;
};
Run Code Online (Sandbox Code Playgroud)
for (Data & data : all)
{
data.update_function(data);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1658 次 |
| 最近记录: |