in C++, how to use a singleton to ensure that each

2019-02-24 06:59发布

问题:

I have a bunch of C++ classes.

I want each class to have something like:

static int unique_id;

All instances of a same class should have the same unique_id; different classes should have different unique_id's.

The simplest way to do this appears to be threading a singleton through the classes.

However, I don't know what's called when for static class members / things that happen before main.

(1) if you have a solution that does not involve using singleton, that's fine too

(2) if you have a solution that gives me a :

int unique_id(); 

that is fine too.

Thanks!

回答1:

Have a class that increments it's ID on each creation. Then use that class as a static field in each object that is supposed to have an 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();
    }
};


回答2:

Building on 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();

Use it like this:

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


回答3:

Actually that's very similar to RTTI. To achieve (2), C++'s buildin RTTI can be exploited. Call typeid on *this, and take the address of the typeinfo as unique ID.

Conss: a) IDs aren't be fixed (recompile would change them), and b) the information is only available given an instance of the class, c) it's ugly.

Why do you want this?



回答4:

C++ has this already built in.

You can use the typeid operator to return a type_info class. The type_info:name() will return the (unique) name of the class.



回答5:

First, why? In any case, you can manually set the IDs easily:

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

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

Then

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

Of course, you'll have to manually keep track of the IDs for each class; at this point, I'll ask the original question: why?



回答6:

I have recently found sbi's version of Kornel's solution to be very useful. Thank you both for providing your answers. However, I wanted to extend the solution further so that several types of IDs can be easily created without creating a separate pair of id_impl and id_base classes for each new type.

To do this I templated the id_impl class, and added another argument to the id_base. The result is encapsulated in a header file that is included anywhere one wants to add a new ID type:

//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();

For my application I wanted several abstract base classes to have an ID type associated with them. So for each instance of the GeneralIDbase template the types specified are: the abstract base class of the derived class being declared, and the derived class being declared.

The following main.cpp is an example:

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

The output of this code is

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

I hope this helps! Please let me know of any issues.