在 C++ 中,如何使用单例来确保每个类都有唯一的整数 ID?

Posted

技术标签:

【中文标题】在 C++ 中,如何使用单例来确保每个类都有唯一的整数 ID?【英文标题】:in C++, how to use a singleton to ensure that each class has a unique integral ID? 【发布时间】:2011-01-11 11:41:31 【问题描述】:

我有一堆 C++ 类。

我希望每个班级都有类似的东西:

static int unique_id;

同一个类的所有实例都应该有相同的unique_id;不同的类应该有不同的unique_id。

执行此操作的最简单方法似乎是将单例线程通过类。

但是,我不知道什么时候调用静态类成员/在 main 之前发生的事情。

(1) 如果你有一个不涉及使用单例的解决方案,那也没关系

(2) 如果你有一个解决方案可以给我一个:

int unique_id(); 

那也不错。

谢谢!

【问题讨论】:

您需要控制值还是只是想区分不同类别的对象?可以只使用 typeid 关键字作为区分符吗? 我不需要控制值(不使用它进行序列化)。我需要区分不同类的对象。 [特别是模拟 Haskell 的 Data 字。] 这就是 std::type_infotypeid 运算符的结果)的发明目的。它甚至具有用作地图键的机制。 小心使用此类 ID 在机器之间交换信息,或将对象序列化到文件或从文件中序列化对象。一台机器生成 ID 的方式可能与另一台机器不同。如果您重新组织代码,生成的 ID 甚至可能在同一台机器上发生变化。 【参考方案1】:

有一个在每次创建时递增其 ID 的类。然后将该类用作每个应该具有 ID 的对象中的静态字段。

class ID

    int id;
public:
    ID() 
        static int counter = 0;
        id = counter++;
    

    int get_id()   return id; 
;

class MyClass

    static ID id;
public:
    static int get_id() 
    
        return id.get_id();
    
;

【讨论】:

-1 类的每个实例都具有相同的值。不同的类有不同的值。 @anon,如果您不理解解决方案,请不要投反对票,要求澄清。 你是对的。这是我搞砸了。你可以“编辑”你的帖子,这样我就可以投票给你了吗? (投票太旧,无法更改)。 @anon,没有冒犯,只是不要过早:) @Korenl 感谢您回来指出我误解了(而不是默默地无视我);你的解决方案很酷。【参考方案2】:

以Kornel's solution 为基础:

class id_impl 
  private:
    id_impl() 
    static int get_next_id()
    
      static int counter = 0;
      return ++counter;
    
    template< class T >
    friend class id_base;
;

template< class T >
class id_base : private id_impl

  public:
    static int get_id()  return id; 
  private:
    static int id;
;

template< class T >
int id_base<T>::id id = get_next_id();

像这样使用它:

class my_class : public id_base<my_class> 
  // ...
;

【讨论】:

额外的复杂功能会给您带来什么? 这只是id_base创建者 的麻烦。对于它的用户来说,这是一种简化:他们只需要从类派生而不必将get_id() 成员复制到他们的所有类中。 啊,好的。这是为了节省大量繁琐的工作而奇怪地重复出现的模板习语。 轻微吹毛求疵,ID&lt;T&gt;::id_base id 应该是int id_base&lt;T&gt;::id。 :) 你的代码没有编译,codepad.org/QipqYlPE你需要改 int id_base::id id = get_next_id();到 int id_base::id = get_next_id();工作版本在这里codepad.org/zckHKJHI【参考方案3】:

实际上这与 RTTI 非常相似。为了实现 (2),可以利用 C++ 的内置 RTTI。在*this上调用typeid,并将typeinfo的地址作为唯一ID。

缺点:a) ID 不是固定的(重新编译会更改它们),b) 信息仅在给定类的实例时可用,c) 它很丑。

你为什么要这个?

【讨论】:

【参考方案4】:

C++ 已经内置了这个。

您可以使用typeid 运算符返回type_info 类。 type_info:name() 将返回类的(唯一)名称。

【讨论】:

您必须启用 RTTI。 如何获取整数而不是字符串? @anon,type_info 不幸的是没有这样的字段(真的很遗憾)。您可以将 type_info 地址类型转换为整数,但这将是一个糟糕的解决方案。 小心。 TTBOMK,std::type_info::name() 的结果未指定。虽然我知道的所有实现都返回了一些有意义的东西,但我认为标准并没有强制要求。实现可以返回 "foo" 或空字符串,但仍符合标准。【参考方案5】:

首先,为什么?在任何情况下,您都可以轻松地手动设置 ID:

template <int id>
struct base  enum  unique_id = id ; ;

class foo: public base<5>  ... ;
class bar: public base<10>  ... ;

然后

foo x;
bar y;
assert(x.unique_id == 5);
assert(y.unique_id == 10);

当然,您必须手动跟踪每个类的 ID;此时,我会问原来的问题:为什么?

【讨论】:

【参考方案6】:

我最近发现Kornel's solution 中的sbi's version 非常有用。谢谢你们两位提供答案。但是,我想进一步扩展解决方案,以便可以轻松创建多种类型的 ID,而无需为每个新类型创建一对单独的 id_impl 和 id_base 类。

为此,我对 id_impl 类进行了模板化,并向 id_base 添加了另一个参数。结果被封装在一个头文件中,该文件包含在任何想要添加新 ID 类型的地方:

//idtemplates.h

template< class T >
class GeneralID 

  private:
    GeneralID() 
    static int GetNextID()
    
      static int counter = 0;
      return ++counter;
    
    template< class T, class U >
    friend class GeneralIDbase;
;

template< class T, class U >
class GeneralIDbase : private GeneralID < T >

  public:
    static int GetID()  return ID; 
  private:
    static int ID;
;

template< class T, class U >
int GeneralIDbase<T, U>::ID = GetNextID();

对于我的应用程序,我希望几个抽象基类具有与之关联的 ID 类型。因此,对于 GeneralIDbase 模板的每个实例,指定的类型是:正在声明的派生类的抽象基类,以及正在声明的派生类。

以下 main.cpp 是一个示例:

//main.cpp    

#include<iostream>
#include<idtemplates.h>

using namespace std;

class MyBaseClassA ;
class MyBaseClassB ;

class MyClassA1 :public MyBaseClassA, public GeneralIDbase<MyBaseClassA, MyClassA1> ;
class MyClassA2 :public MyBaseClassA, public GeneralIDbase<MyBaseClassA, MyClassA2> ;
class MyClassB1 :public MyBaseClassB, public GeneralIDbase<MyBaseClassB, MyClassB1> ;
class MyClassB2 :public MyBaseClassB, public GeneralIDbase<MyBaseClassB, MyClassB2> ;

    int main()

    MyClassA1 objA1;
    MyClassA2 objA2;

    cout << "objA1.GetID() = "  << objA1.GetID() << endl;
    cout << "objA2.GetID() = "  << objA2.GetID() << endl;

    MyClassB1 objB1;
    MyClassB2 objB2;

    cout << "objB1.GetID() = "  << objB1.GetID() << endl;
    cout << "objB2.GetID() = "  << objB2.GetID() << endl;

    cin.get();
    return 0;

这段代码的输出是

/*
objA1.GetID() = 1 
objA2.GetID() = 2 
objB1.GetID() = 1 
objB2.GetID() = 2 
*/

我希望这会有所帮助!如有任何问题,请告诉我。

【讨论】:

以上是关于在 C++ 中,如何使用单例来确保每个类都有唯一的整数 ID?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用单例模式确保服务 “负载均衡” 的唯一性?

c++头文件相互包含

绝大多数类都有构造方法吗

模块加载时如何执行一次?

确保对象的唯一性——单例模式

在 C++ 中,要确保单例线程安全,该怎么做?