2017-01-15 1 views
2

У меня есть класс:Как получить настоящее имя экземпляра класса из вектора?

class IComponent{}; 

И я использую его как базовый класс для других конкретных классов.

class First : public IComponent{public: void First();}; 
class Second : public IComponent{public: void Second();}; 

И у меня есть объект класса, у которого есть вектор, который хранит классы, унаследованные от IComponent.

vector<IComponent*> vectorOfComponents; 

В этом классе у меня есть метод шаблона, который добавляет компонент в вектор.

template<typename comp> 
void AddComponent() { 
    comp * c = new comp(); 
    getComponents().push_back(c); 
} 

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

template<typename comp> 
comp * GetComponent() 
{ 
    comp component; 
    for (int i = 0; i < GetComponentsCount(); i++) 
     if (typeid(*getComponents()[i]).name() == typeid(component).name()) 
      return (comp*)getComponents()[i]; 
    return NULL; 
} 

И когда я потом запустить его:

if (obj.GetComponent<FirstClass>() != NULL) 
     std::cout << "isn't null\n"; 
    else 
     std::cout << "is null\n"; 

Я знаю, этот вектор сохраняет нажатые экземпляры наследуемого класса IComponent, но он всегда печатает меня как null, потому что первый typeid() показывает мне IComponent, а второй показывает конкретный унаследованный компонент.

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

template<typename comp> 
void AddComponent() { 
    comp c = comp(); 
    IComponent * i = &c; 
    getComponents().push_back(i); 
} 
+0

Вы можете добавить чистую виртуальную функцию в 'IComponent', которая возвращает идентификатор. Производные классы затем вынуждены реализовать эту функцию, чтобы вы могли спросить их об их типе. – Unimportant

+4

Интересно, что вы думаете, что это имеет какое-либо отношение к векторам. Типы и объекты не изменяют свои характеристики за счет сохранения в векторе. – juanchopanza

+0

@juanchopanza Типы реализации собственности способны внедрять полиморфизм. Например, 'shared_ptr' работает с динамическим типом своего объекта, даже если он сужается до неполиморфного базового указателя. Также в этом вопросе упоминается полиморфизм шаблона времени компиляции. Короткий длинный рассказ ... Полиморфизм в C++ большой, утомительный и сложный. – Potatoswatter

ответ

6

typeid только работает динамически для полиморфных типов классов. Для того чтобы класс был полиморфным, ему нужна функция virtual. Кроме того, деструктор должен быть virtual, если объект класса может принадлежать указателю базового класса, что может иметь место здесь.

Таким образом, вы должны использовать,

class IComponent{ 
    virtual ~ IComponent() = default; 
}; 

Edit: также, type_info::name() возвращает указатель на строку C, которая не сравнима с использованием ==. Программа, использующая динамические библиотеки, может наблюдать одинаковые строки name на разных адресах (хотя в остальном это необычно). Вместо этого сравните объекты type_info. Кроме того, typeid может принимать тип в качестве операнда; вам не нужно создавать объект для его использования. Итак, вы можете сделать,

if (typeid(*getComponents()[i]) == typeid(comp)) 

Обратите внимание, что это проверяет точно соответствующий тип. Для того, чтобы найти производные объекты, а также (что позволяет comp быть базовым классом), используйте dynamic_cast:

if (comp *p = dynamic_cast<comp *>(getComponents()[i])) { 
    return p; 
} 
+1

Большое спасибо! – Michael

3

Я предлагаю вам рассмотреть другой возможный обходной путь.

Так что вы можете добавить функцию getType в базовый класс:

enum class Types { 
    First, 
    Second 
}; 

class IComponent { 
    public: 
     virtual Types getType() const = 0; 
     virtual ~IComponent() = default; 
}; 

А затем реализовать его в производных классах:

class First : public IComponent { 
public: 
    virtual Types getType() const override { 
     return Types::First; 
    } 
}; 

class Second : public IComponent { 
    public: 
     virtual Types getType() const override { 
      return Types::Second; 
     } 
}; 

После этого поиска и другие задачи, кажется, довольно просто:

const auto it = std::find_if(std::cbegin(components), std::cend(components), 
    [](auto& ptr) { 
     return ptr->getType() == Types::Second; 
    } 
); 

wandbox example

+0

Ничего себе, этот код выглядит великолепно, не могли бы вы объяснить мне, что такое класс enum? Это перечисление, которое содержит тип? И что означает const = 0? "virtual types getType() const = 0;" Что функция является чистой виртуальной и она не влияет на поля экземпляра экземпляра? И что означает: "const override"? – Michael

+0

@Michael Это класс перечисления: http://en.cppreference.com/w/cpp/language/enum –

+0

Я обновил свой комментарий – Michael

0

Убедитесь IComponent имеет, по меньшей мере, один virtual метод (деструктор будет достаточно, что должно быть в любом случае виртуальный, когда дело с наследованием), а затем использовать dynamic_cast вместо typeid.

class IComponent{ 
public: 
    virtual ~IComponent() = default; 
}; 

template<typename ComponentType> 
ComponentType* GetComponent() 
{ 
    auto &comps = getComponents(); 
    for (auto *item : comps) { 
     ComponentType *comp = dynamic_cast<ComponentType*>(item); 
     if (comp) 
      return comp; 
    } 
    return nullptr; 
} 
Смежные вопросы