в С++, как использовать синглтон, чтобы гарантировать, что каждый класс имеет уникальный интегральный идентификатор?

У меня есть куча классов С++.

Я хочу, чтобы каждый класс имел что-то вроде:

static int unique_id;

Все экземпляры одного класса должны иметь одинаковый unique_id; разные классы должны иметь разные уникальные_идентификаторы.

Самый простой способ сделать это, по-видимому, состоит в том, чтобы пропустить синглтон через классы.

Однако я не знаю, что называется, когда для статических членов класса/вещей, которые происходят до main.

(1) если у вас есть решение, не связанное с использованием синглтона, это тоже нормально

(2) если у вас есть решение, которое дает мне:

int unique_id(); 

это тоже хорошо.

Спасибо!


person anon    schedule 31.01.2010    source источник
comment
Вам нужен контроль над значением или вы просто пытаетесь различать объекты разных классов? Можете ли вы просто использовать ключевое слово typeid в качестве отличительного признака?   -  person RickNotFred    schedule 31.01.2010
comment
Мне не нужен контроль над значением (не использовать это для сериализации). Мне нужно различать объекты разных классов. [В частности, эмуляция слова Haskell Data.]   -  person anon    schedule 31.01.2010
comment
Вот для чего был придуман std::type_info, результат оператора typeid. У него даже есть механика, которую можно использовать в качестве ключа карты.   -  person sbi    schedule 31.01.2010
comment
Будьте осторожны при использовании этого идентификатора класса для обмена информацией между машинами или сериализации объектов в/из файла. Одна машина не может генерировать идентификаторы так же, как другая. Сгенерированные идентификаторы могут даже измениться на том же компьютере, если вы реорганизуете свой код.   -  person Emile Cormier    schedule 31.01.2010


Ответы (6)


Имейте класс, который увеличивает свой идентификатор при каждом создании. Затем используйте этот класс в качестве статического поля в каждом объекте, который должен иметь идентификатор.

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();
    }
};
person Kornel Kisielewicz    schedule 31.01.2010
comment
-1 Каждый экземпляр класса имеет одинаковое значение. Разные классы имеют разные значения. - person anon; 31.01.2010
comment
@anon, не минусуйте, если не понимаете решения, попросите разъяснений. - person Kornel Kisielewicz; 31.01.2010
comment
Ты прав. Это мой косяк. Можете ли вы отредактировать свой пост, чтобы я мог вместо этого проголосовать за вас? (Голосование слишком старое, чтобы его можно было изменить). - person anon; 31.01.2010
comment
@anon, не обижайся, только не торопись :) - person Kornel Kisielewicz; 31.01.2010
comment
@Korenl спасибо, что вернулись и указали, что я неправильно понял (вместо того, чтобы молча игнорировать меня); ваше решение очень крутое. - person anon; 31.01.2010
comment
@anon, без проблем, я понимаю, что оператор без кода, возможно, было сложно разобрать: P - person Kornel Kisielewicz; 31.01.2010
comment
Вам нужно определить элемент ID::id. И определение класса заканчивается ; в C++. :o> Кроме того, вы можете поместить всю механику в свой собственный класс, так что все, что вам нужно сделать, это получить от него. См. stackoverflow.com/questions/2172879/2173034#2173034. - person sbi; 31.01.2010
comment
Я думаю, что counter в конструкторе ID должен быть на уровне класса, а не на уровне метода, в основном потому, что я думаю, что идея статики в функции-члене, особенно встроенной, немного сбивает с толку. - person Omnifarious; 31.01.2010
comment
...о да, и get_id() участников, вероятно, должно быть static. - person sbi; 31.01.2010
comment
@sbi, я исправил то, что ты указал. Спасибо - писал по памяти, отсюда и ошибки. - person Kornel Kisielewicz; 31.01.2010

Основываясь на решении Kornel:

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> {
  // ...
};
person sbi    schedule 31.01.2010
comment
Что дает вам дополнительное усложнение? - person Omnifarious; 31.01.2010
comment
Это только осложнение для создателя id_base. Для пользователей это упрощение: им нужно только наследоваться от класса и не нужно копировать этот член get_id() во все свои классы. - person sbi; 31.01.2010
comment
Ага, хорошо. Это странно повторяющаяся идиома шаблона, чтобы сэкономить много утомительной работы. - person Omnifarious; 31.01.2010
comment
Мелкие придирки, ID<T>::id_base id должно быть int id_base<T>::id. :) - person Georg Fritzsche; 31.01.2010
comment
@gf: Спасибо. (Я начал с редактирования ответа Корнеля и забыл изменить его...) - person sbi; 31.01.2010
comment
+1: да, это хорошее расширение, я предоставил только основу решения. - person Kornel Kisielewicz; 31.01.2010
comment
Ваш код не компилируется, codepad.org/QipqYlPE вам нужно изменить int id_base‹T›::id id = получить_следующий_id(); в int id_base‹T›::id = get_next_id(); рабочая версия находится здесь codepad.org/zckHKJHI - person ; 01.02.2010
comment
@sbi ... это потрясающе. Мой кодер стал намного чище после этого. Объясните мне пожалуйста, почему шаблон‹ class T › int id_base‹T›::id id = get_next_id(); не приводит к определению нескольких копий id_base‹T›::id? - person anon; 02.02.2010
comment
@anon: Да - по одному на каждый тип. Я думал, это то, что ты хотел? - person sbi; 04.02.2010
comment
@Beh Tou Cheh: Спасибо за публикацию исправленной версии кода. Я не пробовал компилировать. Простите за это. - person sbi; 04.02.2010

На самом деле это очень похоже на RTTI. Для достижения (2) можно использовать встроенный в C++ RTTI. Вызовите typeid на *this и возьмите адрес typeinfo в качестве уникального идентификатора.

Минусы: а) идентификаторы не исправлены (перекомпиляция изменит их), и б) информация доступна только для данного экземпляра класса, в) это уродливо.

а зачем тебе это?

person Alexander Gessler    schedule 31.01.2010

В C++ это уже встроено.

Вы можете использовать оператор typeid для возврата класса type_info. type_info:name() вернет (уникальное) имя класса.

person doron    schedule 31.01.2010
comment
Однако вам нужно будет включить RTTI. - person doron; 31.01.2010
comment
Как получить целое число вместо строки? - person anon; 31.01.2010
comment
@anon, type_info, к сожалению, не имеет такого поля (позор, правда). Вы можете преобразовать адрес type_info в целое число, но это будет неудобным решением. - person Kornel Kisielewicz; 31.01.2010
comment
Осторожно. TTBOMK, результат std::type_info::name() не указан. Хотя все реализации, которые я знаю, возвращают что-то осмысленное, я не думаю, что стандарт предписывает это. Реализация может возвращать "foo" или пустую строку и при этом соответствовать стандарту. - person sbi; 31.01.2010

Во-первых, почему? В любом случае вы можете легко установить идентификаторы вручную:

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

Конечно, вам придется вручную отслеживать идентификаторы для каждого класса; в этот момент я задам исходный вопрос: почему?

person Jesse Beder    schedule 31.01.2010

Недавно я нашел версию sbi решение Kornel будет очень полезным. Спасибо вам обоим за ваши ответы. Однако я хотел еще больше расширить решение, чтобы можно было легко создавать несколько типов идентификаторов без создания отдельной пары классов id_impl и id_base для каждого нового типа.

Для этого я создал шаблон класса id_impl и добавил еще один аргумент в id_base. Результат инкапсулируется в заголовочный файл, который подключается везде, где нужно добавить новый тип идентификатора:

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

Для моего приложения я хотел, чтобы несколько абстрактных базовых классов имели связанный с ними тип идентификатора. Таким образом, для каждого экземпляра шаблона 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 
*/

Надеюсь, это поможет! Пожалуйста, дайте мне знать о любых проблемах.

person NauticalMile    schedule 29.07.2012