Уникальный идентификатор, определяемый самым производным классом, доступный через базовый класс - PullRequest
2 голосов
/ 18 марта 2010

Хорошо, идея в том, что у меня есть карта «компонентов», которые наследуются от componentBase и имеют ключ к идентификатору, уникальному для самого производного *.

Только я не могу придумать хороший способ заставить это работать. Я попробовал это с конструктором, но это не работает (возможно, я сделал это неправильно). Проблема с любыми виртуальными и т. Д. Трюками наследования заключается в том, что пользователь должен указать их внизу, что можно забыть и сделать их менее ... чистыми.

* Правильная фраза? Если -> наследование; foo является наиболее производным: foo-> foo1-> foo2-> componentBase

Вот код, показывающий проблему, и почему CRTP не может ее устранить: (Нет, это не законный код, но я пытаюсь обдумать свои мысли)

#include<map>

class componentBase
{ public: virtual static char idFunction() = 0; };

template <class T>
class component
    : public virtual componentBase
{
public:
    static char idFunction(){ return reinterpret_cast<char>(&idFunction); }
};

class intermediateDerivations1
    : public virtual component<intermediateDerivations1>
{
};

class intermediateDerivations2
    : public virtual component<intermediateDerivations2>
{   };

class derived1
    : public intermediateDerivations1
{   };

class derived2
    : public intermediateDerivations1
{   };


//How the unique ID gets used (more or less)
std::map<char, componentBase*> TheMap;

template<class T>
void addToMap(componentBase * c)
{
    TheMap[T::idFunction()] = c;
}

template<class T>
T * getFromMap()
{
    return TheMap[T::idFunction()];
}

int main()
{

    //In each case, the key needs to be different.

    //For these, the CRTP should do it:
    getFromMap<intermediateDerivations1>();
    getFromMap<intermediateDerivations2>();

    //But not for these.
    getFromMap<derived1>();
    getFromMap<derived2>();

    return 0;
}

Более или менее мне нужно что-то, что всегда рядом, независимо от того, что делает пользователь, и имеет сортируемое значение, уникальное для самого производного класса.

Кроме того, я понимаю, что это не самый часто задаваемый вопрос, на самом деле у меня возникли некоторые неожиданные затруднения, оборачивающиеся вокруг него словами, поэтому задавайте вопросы, если / когда вам понадобятся разъяснения.

Edit: Использование бета-формулировки; class производный2 имеет идентификационный номер, уникальный среди всех классов, производных от ComponentBase, и из которых не производны никакие другие классы - за исключением того, что не должно быть случаев использования, в которых мы имеем дело с экземпляром, когда не знаю наиболее производный тип. То есть мы никогда не должны иметь дело с foo1*, который фактически указывает на `` foo`.

Каждый раз, когда мне нужно получить доступ к этому идентификатору, у меня есть информация о типе наиболее производного класса; через шаблонный характер addComponent, getComponent и removeComponent.

Хм, иначе говоря; Мне нужно «преобразовать» тип в уникальное число, пока я знаю тип, чтобы позже я мог различить две вещи, когда у меня нет информации о типе.

Ответы [ 2 ]

0 голосов
/ 02 августа 2011

Вы можете использовать оператор typeid.

Если у вас есть компонент ComponentBase *, тогда

typeid(*component)

вернет type_info & object, который однозначно идентифицирует класс объекта, на который указывает указатель компонента. (Если компонент указывает на объект Derived, то он вернет объект type_info, принадлежащий классу Derived. Обратите внимание, что typeid (component) вернет type_info &, представляющий тип ComponentBase *, поэтому разыменовываем указатель важен.)

Тогда вы можете использовать, например. адрес этого объекта type_info или результат type_info :: name () в качестве ключа для вашей карты.

0 голосов
/ 18 марта 2010

Я не понимаю, почему вы используете reinterpret_cast в class component.

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

С другой стороны, каждый класс должен реализовывать статический clone или create метод. фабрика будет иметь map. Указатель на функцию указывает на определенный метод класса create или clone. Поскольку std::map не может быть создан как постоянный статический объект во время компиляции, я обычно использую постоянные статические массивы для хранения идентификаторов и указателей функций. Если массив небольшой, он незначителен по производительности до map.

Пример:

class Base
{;};

// Declare a synonym for a pointer to the creation function.
typedef Base *    (*P_Creation_Function)(unsigned int id);

struct Creation_Entry
{
    unsigned int         class_id;
    P_Creation_Function  p_creator;
};

class Child1 : public Base
{
  public:
    static Base * create(unsigned int id);
};

Creation_Entry  creation_table[] =
{
    {1, Child1::create},
};

static const unsigned int NUM_CREATORS =
    sizeof(creation_table) / sizeof(creation_table[0]);

//  Process 1:  search table for ID
for (unsigned int i = 0; i < NUM_CREATORS; ++i)
{
    if (creation_table[i].class_id == new_id)
    {
        return (*creation_table[i].p_creator)(new_id);
    }
}

//  Process 2:  Execute each creation function in the table.
//  Creation functions will return NULL if the ID is not a match
Base * p_new_object;
for (unsigned int j = 0; j < NUM_CREATORS; ++j)
{
    p_new_object = (*creation_table[j].p_creator)(new_id);
    if (p_new_object)
    {
        return p_new_object;
    }
}

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

Я использовал оба процесса и реализую их в зависимости от своего настроения и размера проекта. : -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...