当我需要修改一个键时,任何避免在std::map中重新分配的方法

Any way to avoid reallocation in std::map when I need to modify a key?

本文关键字:map std 新分配 方法 分配 修改 一个 任何避      更新时间:2023-10-16

我需要修改std::map中一个元素的键。
目前,我通过擦除元素并使用不同的键重新插入它来做到这一点。
不幸的是,这是缓慢的——它涉及到额外的堆释放和重新分配。

当我需要修改元素的键时,避免自动节点重新分配和销毁的最好方法是什么?

我认为没有办法避免节点被释放和重新分配。

我能想到的最好的方法是指定一个带有lookaside列表的Allocator,这样当一个节点在被释放后很快被重新分配时,您可以非常快速/轻松地提供相同的节点。

如果您确定键的更改不会影响键的相对顺序,则可以在适当的位置修改键(例如,通过将其指定为可变成员),但这样做会导致未定义的行为——很有可能您可以避开它,但是它仍然是官方未定义的行为。

您至少可以根据您映射的类的内部构造部分进行教训。考虑下面的例子:

class A
{
    public:
    A(){};
    A(int a)
    : a_(a)
    {
        std::cout << "construct" << std::endl;
    }
    A& operator=(const A& b)
    {
       a_ = b.a_;
       std::cout << "=" << std::endl;
    }
    A(A&& o)
    : a_(o.a_)
    {
        std::cout << "moved" << std::endl;
    }
private:
    int a_;
};

有一个move构造函数。通过使用map.emplace,您可以跳过类成员的重建:

std::map<int, A> mapper;
mapper[1] = A(1);
mapper[2] = A(2);
mapper.emplace(3, std::move(mapper[2]));
mapper.erase(mapper.find(2));

这将打印以下内容:

construct
=
construct
=
moved

表示前两个被构造,但第二个被移动。您仍然需要删除元素,并注意move语义。

如果您的int a_被替换为一些大的东西,您将通过移动数据和不删除任何内容来减少深度复制时间和删除时间。

这可能会改变您的设计,但可以考虑使用指针(或者更好的是shared_ptr)作为键。这样就可以在不更新键的情况下更新底层数据。

相关文章: