C++中的惯用"guaranteed-unique"标识符

Idiomatic "guaranteed-unique" identifiers in C++

本文关键字:guaranteed-unique 标识符 C++      更新时间:2023-10-16

是否有一种习惯的c++方法来保留和回收保证唯一的标识符?我的要求是:

  1. 假设存在当前未保留的ID, reserve_id(void)返回该ID给我。
  2. 在不间断的reserve_id()调用序列中,没有单个标识符将返回两次
  3. 存在一个函数recycle(id_type)返回一个标识符到可用池。
例如,

我看到Boost::Uuid,但是a)我没有看到断言两个Uuid保证唯一性的文档,b)我暂时被限制在较早版本的Boost(1.40)中。我可以推动升级,如果这是特别完美的任务。

我认为您已经通过查找Boost::Uuid解决了这个问题,除了您需要回收已经生成的标识符。

从你在问题中链接到的文档中:

当uuid由定义的机制,它们要么是保证是独一无二的,与众不同的从所有其他生成的uuid(即它以前从未被生成过吗它将永远不会再生成),或者它极有可能是独一无二的(取决于机制)。

如果您一心想回收和重用现有标识符,我想您可以随着时间的推移建立一个uuid池,只有在需要时才生成新的uuid,并且发现池是空的。但是我想象不出比生成新的UUID更好的场景。

EDIT:您已经评论过需要保证唯一性。实际上,在以编程方式生成唯一标识符时,您永远不会得到唯一标识符。在实践中,您将把生成的ID存储在具有有限大小的数据类型中,因此您可以生成的ID集合也是有限的。恕我直言,你能做到的最好的是在一个容忍阈值内模拟唯一性。

你可以通过

  • 使用一种技术,使得获得重复UUID的机会非常渺茫(这就是Boost::UUID将要做的);

  • 将极有可能是唯一的UUID的生成封装在其他逻辑中,该逻辑在已经生成的UUID列表中查找新生成的UUID,以消除新生成的UUID重复的微小可能性。显然,当您在列表中处理非常大量的uid时,这样做的实用性会降低。你预计会产生多少?

  • 如果你想要真正大量的唯一id,比本地类型所能容纳的大,你可以实现一个类型来管理内存和做必要的数学运算,只产生顺序的id,或者你可以使用像GNU Bignum Library这样的东西来为你做这件事。

id的有效期是多长?你真的需要回收它们吗,或者你能永远让它们独一无二吗?你需要一次生成多少个?你能在本我身上投入多少比特?

这里有一个简单的方法:拿你以太网卡的mac地址(这是全局唯一的硬件问题),混合时间/日期(以毫秒为分辨率)和一个递增的整数计数器(每生成一个id递增一次),你就会有一个id在你的时间/日期范围内是唯一的,只要你不在这台机器上的一毫秒内生成MAXINT。现在它不是随机的,攻击者很容易预测,所以不要为了安全而使用它,它肯定不是最有效的比特使用,但它是全球唯一的。

你需要什么样的唯一性?
只是在程序的生命周期中唯一,还是在多个运行/跨进程中唯一?

如果是前者,那么可以new一个字节的内存,然后使用该内存的地址作为您的标识符。这将保证在您delete内存之前是唯一的,此时它可能被回收。

这可以很容易地包装成这样的类:

#include <stdint.h>
class UID
{
public:
        typedef uint64_t id_type;
        static const id_type reserve_id()
        {
                uint8_t* idBlock = new uint8_t;
                *idBlock = validId;
                return (id_type)idBlock;
        }
        static void recycle(id_type id)
        {
                uint8_t* idBlock = (uint8_t*)id;
                if (*idBlock == validId)
                {
                        *idBlock = 0;
                        delete idBlock;
                }
        }
private:
        static const uint8_t validId = 0x1D;
};

可能有点不寻常,但如果您只需要每个进程的唯一性,则满足您的要求:)

是的,这很简单。

  1. reserve_id功能为operator new(0)
  2. 分配零字节,但有一个唯一的地址。recycle函数当然是operator delete

这个问题似乎与c++无关,它更像是一个基本问题。在任何给定时间有多少id是有效的?如果您希望在任何给定时间都没有多少有效的id,只需根据您的性能需求和相对回收/保留频率将它们放入诸如链表、向量或集合之类的容器中。排序链表可能是最好的选择,因为您将在O(n)内进行回收和保留操作。一个向量分别有O(n), O(n log n)和一个集合分别有O(n log n), O(n)(可能是错的,我想得很快)

void recycle(ID) {
    container.remove(ID);
    // abort if unsuccessiful (= invalid ID)
}
ID reserve() {
    static ID last = 0;
    while(container.find(last)) {
        last++;
    }
    return last;
}