为类的每个实例分配唯一 ID

Assigning Unique ID's to each instance of a class

本文关键字:分配 唯一 ID 实例      更新时间:2023-10-16

我正在从XML文件导入项。每个XML元素(FoodItem, Person, Order, CoffeeRun)都是一个类&这些元素中的每一个都有一个唯一的ID(该类的唯一ID)。

<person>
<id>0</id>
<name>...</name>
</person>
<FoodItem>
<id>0</id>
<name>Coffee</name>
</FoodItem>

我正试图开发一个子类DatabaseItem,这确保一个类的2个对象具有相同的ID。你能帮我,帮我开发一个有效的算法来确保没有对象具有相同的ID吗?

我的两种方法对我来说似乎有点低效:

  • 使用包含到目前为止所有USED id的静态类向量。当创建一个新的DatabaseID( int requestedID )对象时,我检查ID是否可用,通过检查向量中所有使用的值来检查ID是否已经存在,我认为这是大o 'n速度?

  • 使用静态类bool vector,其中vector的每个元素对应于一个id(因此vector[1]将对应于id为1的对象)。我检查是否已经通过查看vector中的元素是否为true if ( v[nID] == true ) { // this ID is already taken }来获取id。这看起来效率很低,因为这意味着我的vector将占用大量内存,对吗?

  • 我不熟悉在c++中使用地图,但也许我应该在这里使用一个?

任何关于有效算法的建议都将非常有帮助:

class DatabaseItem
{
    public:
        static unsigned int instanceCount;
        DatabaseItem()
        {
            // Assign next available ID
        }
        DatabaseItem( unsigned int nID )
        {
            // Check that that id is not already taken
            // if id is taken, look for next available id &
            // give the item that id
        }
    private:
        unsigned int uniqueID;
};
// My solution: Do you have any better ideas that ensure no objects jave the same ID?
// This seems REALLY inefficient...
class DatabaseItem
{
    public:
        static unsigned int instanceCount;
        static vector <unsigned int> usedIDs;
        DatabaseItem()
        {
            DatabaseItem::instanceCount++;
            uniqueID = instanceCount;
            usedIDs.add( instanceCount );
        }
        DatabaseItem( unsigned int nID )
        {
            if ( isIDFree( nID ) )
            {
                uniqueID = nID;
            }
            else uniqueID = nextAvailableID();
            DatabaseItem::instanceCount++;
        }
        bool isIDFree( unsigned int nID )
        {
            // This is pretty slow to check EVERY element
            for (int i=0; i<usedIDs.size(); i++)
            {
                if (usedIDs[i] == nID)
                {
                    return false;
                }
            }
            return true;
        }
        unsigned int nextAvailableID()
        {
            while ( true )
            {
                unsigned int ID = 0;
                if ( isIDFree( ID ) )
                {
                    return ID;
                }
                else ID++;
            }
        }
    private:
        unsigned int uniqueID;
};

// Alternate that uses boolean vector to track which ids are occupied
// This means I take 30000 boolean memory when I may not need all that
class DatabaseItem
{
    public:
        static unsigned int instanceCount;
        static const unsigned int MAX_INSTANCES = 30000;
        static vector <bool> idVector;
        // Is this how I initialise a static class vector...? (note this code will be outside the class definition)
        // vector <bool> DatabaseItem::idVector( MAX_INSTANCES, false );
        DatabaseItem()
        {
            uniqueID           = nextAvailableID();
            idVector[uniqueID] = true;
        }
        DatabaseItem( unsigned int nID )
        {
            if ( nID >= MAX_INSTANCES )
            {
                // not sure how I shd handle this case?
            }
            if ( idVector[nID] == false )
            {
                uniqueID      = nID;
                idVector[nID] = true;
            }
            else
            {
                uniqueID           = nextAvailableID();
                idVector[uniqueID] = true;
            }
            instanceCount++;
        }
        unsigned int nextAvailableID()
        {
            for (int i=0; i<idVector.size(); i++)
            {
                if ( !idVector[i] )
                {
                    return i;
                }
            }
            return -1;
        }
        bool isIDFree( unsigned int nID )
        {
            // Note I cannot do this: Because I am using Mosync API & it doesn't support any C++ exceptions'
            // I declare idVector with no size! so not idVector( 30000, false)... just idVector;
            // then I allow an exception to occur to check if an id is taken
            try
            {
                return idVector[nID];
            }
            catch (...)
            {
                return true;
            }
        }
    private:
        unsigned int uniqueID;
};

vector<bool>是用每个bool值一个比特来实现的,所以它不会像你想象的那样浪费那么多空间。

set<unsigned int>是一个简单的解决方案。vector<bool>更快。两者都需要一点内存。根据您的使用模式,还有一些其他的解决方案:

  • 一个unsigned int all_taken_upto_this;和一个set<int>组合在一起,覆盖了所有比all_taken_upto_this高的古怪ID -从集合中删除并在可能的情况下增加计数器

  • 逻辑上被视为已取序列或自由序列的begin,endmap<unsigned int, unsigned int>。要正确实现这一点需要一点时间(例如,当你在两个元素之间添加最后一个ID时合并连续的地图元素)

你可以使用一个预制的"sparse bitset"类型的数据结构——我不知道OTOH的任何实现。

根据元素的数量和其他一些问题,您可能会考虑将它们(或至少是指向它们的指针)实际存储在映射中。这将是相当简单的实现,但将占用一些空间。另一方面,它将通过id为您提供快速查找,如果XML中有交叉引用,这可能是一个明显的优势。映射(假设有指针)如下所示:

std::map<int, std::shared_ptr<Object> > id_map;
std::shared_ptr<Object> p( new Object( xml ) );
if ( !id_map.insert( std::make_pair( p->id, p ) ).second ) {
   // failed to insert, the element is a duplicate!!!
} 

如果您没有被锁定使用整数,您可以查看guid(全局唯一id)。根据所使用的平台,通常可以找到一些实用程序函数来动态生成GUID。如果使用Visual Studio,我使用了CoCreateGuid函数。

如果你被锁定为32位整数,另一个选择是哈希表。如果每个XML元素都是唯一的,那么散列函数可以生成唯一的散列值。根据数据集的大小,仍然有很小的碰撞概率。我用过的这个与我使用过的数据集碰撞率很低的叫做Jenkins哈希函数