修改集合内对象的可变成员是否安全?
我很好奇以下场景是否安全。
我有以下类定义:
class ActiveStatusEffect
{
public:
StatusEffect* effect;
mutable int ReminaingTurns;
ActiveStatusEffect() : ReminaingTurns(0)
{
}
//Other unimportant stuff down here
}
然后,我将一组此类定义存储在 std::set 中,如下所示:
struct ASECmp
{
bool operator ()(const StatusEffects::ActiveStatusEffect &eff1, const StatusEffects::ActiveStatusEffect &eff2)
{
return eff1.effect->GetPriority() < eff2.effect->GetPriority();
}
};
std::set<StatusEffects::ActiveStatusEffect, ASECmp> ActiveStatusEffects;
我将 RemainingTurns 标记为可变,因为我希望能够更改它,而不必不断擦除/插入到集合中。即
void BaseCharacter::Tick(Battles::BattleField &field, int ticks)
{
for (auto effect = ActiveStatusEffects.begin(); effect != ActiveStatusEffects.end();)// ++index)
{
auto next = effect;
++next;
if (effect->effect->HasFlag(StatusEffects::STATUS_FLAGS::TickEffect) && effect->ReminaingTurns > 0)
{
effect->effect->TickCharacter(*this, field, ticks);
--effect->ReminaingTurns;
}
if (effect->ReminaingTurns == 0)
{
ActiveStatusEffects.erase(effect);
}
effect = next;
}
}
我很担心,因为这似乎可能会扰乱集合内的顺序,这意味着我不能保证集合将始终按效果排序->GetPrority()
如果这是真的,是否有一种安全的方法(例如没有 RemainingTurns 形成键的一部分)除了复制、修改、擦除然后插入我需要更改的内容之外还要执行此操作?
编辑:
@ildjarn - 抱歉,我认为这并不重要。它只是返回一个存储在 StatusEffect 中的 int。该 int 保证在程序运行时不会改变。
int StatusEffect::GetPriority() const
{
return StatusPriority;
}
I was curious as to whether the following scenario is safe.
I have the following class definitions:
class ActiveStatusEffect
{
public:
StatusEffect* effect;
mutable int ReminaingTurns;
ActiveStatusEffect() : ReminaingTurns(0)
{
}
//Other unimportant stuff down here
}
I then store a group of these inside an std::set as follows:
struct ASECmp
{
bool operator ()(const StatusEffects::ActiveStatusEffect &eff1, const StatusEffects::ActiveStatusEffect &eff2)
{
return eff1.effect->GetPriority() < eff2.effect->GetPriority();
}
};
std::set<StatusEffects::ActiveStatusEffect, ASECmp> ActiveStatusEffects;
I mark RemainingTurns as mutable because I want to be able to change it without haing to constantly erase/insert into the set. I.e.
void BaseCharacter::Tick(Battles::BattleField &field, int ticks)
{
for (auto effect = ActiveStatusEffects.begin(); effect != ActiveStatusEffects.end();)// ++index)
{
auto next = effect;
++next;
if (effect->effect->HasFlag(StatusEffects::STATUS_FLAGS::TickEffect) && effect->ReminaingTurns > 0)
{
effect->effect->TickCharacter(*this, field, ticks);
--effect->ReminaingTurns;
}
if (effect->ReminaingTurns == 0)
{
ActiveStatusEffects.erase(effect);
}
effect = next;
}
}
I'm concerned because it seems possible for this to mess up the ordering within the set, meaning I can't guarantee the set will always be sorted by effect->GetPrority()
If that's true, is there a safe way (such as not have RemainingTurns form part of the key) to do this besides copying, modifying, erasing then inserting what I need to change?
EDIT:
@ildjarn - sorry, I didn't think that mattered. It just returns an int stored within StatusEffect. That int is guaranteed not to change over the runtime of the program.
int StatusEffect::GetPriority() const
{
return StatusPriority;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
更改影响对象排序的数据确实会破坏关联容器的不变量,但由于
ActiveStatusEffect::ReminaingTurns
不涉及ActiveStatusEffect
对象的排序,因此它是可变的,修改它的值是完全无害的。这是一个
std::set
;它怎么能按照ASECmp
定义的标准以外的任何标准进行排序?Changing data that affects the ordering of an object will indeed break the invariants of associative containers, but because
ActiveStatusEffect::ReminaingTurns
is not involved in the ordering ofActiveStatusEffect
objects whatsoever, keeping itmutable
and modifying its value is perfectly harmless.It's a
std::set<StatusEffects::ActiveStatusEffect, ASECmp>
; how could it sort by any criteria other than that defined byASECmp
?如果你改变了 std::set 中某些东西的键,你就会进入未定义行为的境地 - 就这么简单。它不仅会“打乱顺序”,而且该装置可能会完全停止正常工作。
If you change the key of something in a std::set you are off in Undefined Behaviour land - simple as that. Not only will it "mess up the ordering", but the set will probably stop working correctly altogether.
如果键与实际对象无关,或者只是其中的一部分,那么您应该考虑使用映射而不是集合:
代码的其他问题是您应该使用某种封装(用户代码不应该访问类的内部结构(即成员不应该是公共的)。
If the key is unrelated to the actual object, or only a part of it, then you should consider using a map rather than a set:
Other issues with your code is that you should use some encapsulation (user code should not get access to the internals of the class (i.e. members should not be public).