在集合内修改对象的可变成员是否安全

Is it safe to modify mutable members of objects inside sets?

本文关键字:成员 是否 安全 集合 修改 对象      更新时间:2023-10-16

我很好奇以下情况是否安全。

我有以下类定义:

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;

我将 RemainingTurn 标记为可变,因为我希望能够更改它而不会不断擦除/插入到集合中。 即

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;
    }
}

我很担心,因为这似乎可能会弄乱集合中的排序,这意味着我不能保证集合将始终按 effect->GetPrority() 排序

如果这是真的,除了复制、修改、擦除然后插入我需要更改的内容之外,是否有一种安全的方法(例如没有 RemainingTurn 构成密钥的一部分)来执行此操作?

编辑:

@ildjarn - 对不起,我认为这无关紧要。它只返回存储在 StatusEffect 中的 int。该 int 保证不会在程序运行时更改。

int StatusEffect::GetPriority() const
{
    return StatusPriority;
}

更改影响对象排序的数据确实会破坏关联容器的不变量,但由于ActiveStatusEffect::ReminaingTurns不参与ActiveStatusEffect对象的排序,因此保持其mutable并修改其值是完全无害的。

我很担心,因为这似乎可能会弄乱集合中的排序,这意味着我不能保证集合将始终按 effect->GetPrority() 排序

这是一个std::set<StatusEffects::ActiveStatusEffect, ASECmp>;除了ASECmp定义的标准之外,它怎么能按任何标准进行排序?

如果你在 std::set 中更改某些东西的键,你就在未定义的行为土地上关闭了 - 就这么简单。它不仅会"弄乱排序",而且该集可能会完全停止正常工作。

如果键与实际对象无关,或者只是它的一部分,那么您应该考虑使用映射而不是集合:

std::map< int, ActiveStatusEffect > m;
ActiveStatusEffect x = create();
m[ x.effect->GetPriority ] = x;      // !!!
代码

的其他问题是你应该使用一些封装(用户代码不应该访问类的内部(即成员不应该是公共的)。