TypeID для производных классов общего базового класса - PullRequest
6 голосов
/ 26 ноября 2010

Я пытаюсь реализовать некоторый механизм в C ++, в соответствии с которым всем классам, производным от общего базового класса, присваивается уникальный «идентификатор класса». Например:

class BaseClass  
{  
    //...
    public: unsigned int GetID( void );
    //...
};
class DerivedClass : public BaseClass
{
}

Класс DerivedClass и все другие дочерние элементы BaseClass должны иметь возможность возвращать уникальные идентификаторы без добавления дополнительного кода в DerivedClass ... Однако C ++ делает это довольно трудным для меня. Любые идеи будут оценены.

Заранее спасибо! --- Dan

Ответы [ 4 ]

2 голосов
/ 26 ноября 2010

Вы должны слушать Альфа :) Вот мой анализ: в чистом мире идентификация реализаций угрожает полиморфизму виртуальной функции, не может требоваться и не должна быть необходима.

В грязном мире реального программирования у вас могут быть некоторые причины уникальной идентификации, такие как маршалинг данных на диск, идентификация диагностических сообщений, отслеживание потока управления, накопление статистики использования и т. Д.

Если ваши мысли чисты, ваш дизайн неправильный. Уходите и выясните, почему вы не можете требовать уникальный идентификатор.

Если ваши мысли искажены действительностью, то вы уже готовы заплатить цену производительности и памяти, чтобы удовлетворить ваши требования, а затем по спецификации стоит заплатить за использование встроенных языковых функций, поскольку практически единственный способ достичь вашей цели - предоставить неинвазивную идентификационную услугу. Под неинвазивным я имею в виду, что вам не нужно ничего добавлять к каждому производному классу. Очевидно, что-то должно быть добавлено, поэтому, если вы не хотите этого делать, у вас нет другого выбора, кроме как принять то, что компилятор добавляет для вас.

Основная проблема здесь заключается в том, что если вы используете динамически загружаемые разделяемые библиотеки (DLLS), RTTI может работать не так, как ожидалось. Это не только отрицательно влияет на typeid, но также может предотвратить отлов исключений, которые вы ожидаете поймать (меня укусили!). Может потребоваться некоторая осторожность для обеспечения уникального создания vtables и других RTTI. Это может означать, например, что если vtables зацепит ваш деструктор, он не будет встроенным, потому что в этом случае он может быть сгенерирован более чем в одном месте, разрушая уникальность. В связи с отсутствием поддержки стандартизации ISO для динамической загрузки здесь может потребоваться некоторая информация.

2 голосов
/ 26 ноября 2010

Вы не указываете, что знакомы с typeid и dynamic_cast.

Скорее всего, они решат вашу проблему.

Если нет, пожалуйста, опишите причину, почему нет.

Приветствия и hth.,

1 голос
/ 26 ноября 2010

Как говорит Альф, в этом не должно быть необходимости. typeid уже дает уникальный идентификатор класса, хотя идентификатор не является целым числом. Просто для смеха, если мне позволят ослабить условие «общего базового класса», то:

inline unsigned int counter() {
    static unsigned int count = 0;
    return ++count;
}

struct BaseClass {
    virtual unsigned int GetID() = 0;
    virtual ~BaseClass() {}
};

template <typename D>
struct IntermediateClass : BaseClass {
    virtual unsigned int GetID() {
        static unsigned int thisid = counter();
        return thisid;
    }
};

// usage
struct Derived : IntermediateClass<Derived> {
    ...
};

Вам необходимо добавить безопасность потоков в counter, если она будет использоваться в многопоточных программах.

Очевидно, что идентификатор является уникальным только при данном запуске программы.

Это немного затруднительно, если ваша иерархия наследования глубока и если у вас много конструкторов с разными сигнатурами для разных классов, потому что вам нужно вставить IntermediateClass между каждым производным классом и его прямым базовым классом. Но из всего этого всегда можно выручить:

inline unsigned int counter() {
    static unsigned int count = 0;
    return ++count;
}

struct BaseClass {
    virtual unsigned int GetID() = 0;
    virtual ~BaseClass() {}
};

template <typename D>
unsigned int ThisID(const D *) {
    static unsigned int thisid = counter();
    return thisid;
}

// usage
struct Derived : BaseClass {
    // this single line pasted in each derived class
    virtual unsigned int GetID() { return ThisID(this); }
    ...
};

Я полагаю, что здесь есть «возможность» для новой языковой функции: виртуальная функция, которая определена в базовом классе как функция шаблона с одним параметром шаблона «typename» и которая автоматически переопределяется в каждом производном классе, используя это производный класс в качестве аргумента шаблона. Мнимый синтаксис, поскольку функции виртуального шаблона недопустимы:

struct BaseClass {
    template <typename Derived>
    virtual unsigned int GetID() {
        static unsigned int thisid = counter();
        return thisid;
    }
    virtual ~BaseClass() {}
};

Трудно обосновать языковую особенность на основании желания заново внедрить RTTI, ум ...

0 голосов
/ 26 ноября 2010

Это требует модификации производных классов, но если отсутствие доверия к RTTI является единственной причиной, чтобы избежать хорошо установленного маршрута typeid, dynamic_cast, THEN

Что-то вроде этого должно быть хорошей ставкой. Он также возвращает int по сравнению с type_info RTTI.

class BaseClass{   
    //... 
    public:
       virtual unsigned int GetID( void ); 
    //... 
};

class DerivedClass : public BaseClass{
public: 
   virtual unsigned int GetID( void );
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...