Знать класс подкласса в С++
Я не делал С++, по крайней мере, 7 лет, и внезапно опустился в С++. Мне бы хотелось, чтобы некоторые рекомендации были следующими:
У меня есть класс под названием Animal, и у меня есть 3 класса, которые унаследованы от Animal: Cat, Dog and Bird. Я создал объект списка и использую его для хранения типа Animal.
Этот список может содержать собак кошек и птиц, когда я повторяю этот список животных, я хотел бы знать ближайший тип каждого животного (будь то кошка, собака или птица).
Когда я говорю typeid(animal).name();
, он дает мне Animal, что верно, но я хотел бы знать, что такое Animal.
Любые идеи? Должен ли я использовать перечисления?
Ответы
Ответ 1
Ты почти наверняка не хочешь знать. То, что вы должны сделать, объявить как виртуальные соответствующие методы для взаимодействия с этими животными.
Если вам нужно оперировать их специально, вы можете использовать шаблон посетителя для передачи объекта посетителя или использовать правильные данные в каждом конкретном классе. Если вы настаиваете на наличии тегов (и я подчеркиваю, что это третий вариант - другие два решения оставят ваш код намного чище), у вас есть виртуальный метод под названием classname
, который возвращает идентификатор типа (будь то строка или int или без разницы).
Обратите внимание также на точку резки, если у вас есть массив типа объекта, в отличие от типа указателя. Если вы не использовали С++ через 7 лет, вы можете не знать о расширении использования шаблонов, чтобы сделать язык намного лучше. Просмотрите библиотеки, например boost, чтобы узнать, что можно сделать, и как шаблон позволяет вам писать генерируемый тип генерируемого кода.
Ответ 2
Dog* dog = dynamic_cast<Dog*>(myAnimalPointer);
if (dog == NULL)
{
// This animal is not a dog.
}
Ответ 3
Необходимость знать конкретный тип конкретного объекта обычно является запахом дизайна на С++, поэтому я собираюсь предположить, что вы не пытаетесь это сделать.
Вместо этого создайте абстрактный (чистый виртуальный) интерфейс в Animal
, который описывает функциональность, которую вы хотите иметь у ваших животных. Затем вы можете использовать динамическую отправку для вызова этой функции, даже не требуя знать динамический тип объекта. При необходимости вы всегда можете создавать частные не виртуальные вспомогательные функции в дочерних классах.
Также обратите внимание, что вам нужно сохранить указатель Animal
по (умному) в контейнере, а не по значению. Если вы сохраните их по значению, все они будут нарезаны при вставке в список, потеряв информацию о динамическом типе.
И как отметил @Marcin, использование шаблона посетителя для двойной отправки может быть лучшим подходом, если вам действительно нужно вызывать определенные методы для определенных дочерних классов.
Ответ 4
В зависимости от конкретного кода typeid
возвращает разные вещи. Кроме того, name()
может вернуть что угодно (включая создание первой буквы в верхнем регистре или удаление *), это только для отладки. Теперь у меня есть несколько разных возможных ответов, которые может вернуть typeid(animal).name()
.
Версия 1 animal
- это имя класса:
struct animal {
virtual ~animal() {}
};
struct dog
: animal
{};
struct cat
: animal
{};
struct bird
: animal
{};
int main() {
std::cout << typeid(animal).name() << std::endl; // animal
return 0;
}
Версия 2 animal
- это typedef to animal
:
struct Animal {
};
struct Dog
: Animal
{};
struct Cat
: Animal
{};
struct Bird
: Animal
{};
int main() {
typedef Animal animal;
std::cout << typeid(animal).name() << std::endl; // Animal
return 0;
}
Vesion 3 animal
- это указатель:
struct Animal {
};
struct Dog
: Animal
{};
struct Cat
: Animal
{};
struct Bird
: Animal
{};
int main() {
Dog d;
Animal* animal=&d;
std::cout << typeid(animal).name() << std::endl; // Animal*
return 0;
}
Версия 4 animal
- это объект:
struct Animal {
};
struct Dog
: Animal
{};
struct Cat
: Animal
{};
struct Bird
: Animal
{};
int main() {
Animal animal;
std::cout << typeid(animal).name() << std::endl; // Animal
return 0;
}
Версия 6 animal
является ссылкой на объект не полиморфный:
struct Animal {
};
struct Dog
: Animal
{};
struct Cat
: Animal
{};
struct Bird
: Animal
{};
int main() {
Dog d;
Animal& animal=d;
std::cout << typeid(animal).name() << std::endl; // Animal
return 0;
}
и версия 7 animal
- ссылка на полиморфный объект:
struct Animal {
~virtual Animal() {}
};
struct Dog
: Animal
{};
struct Cat
: Animal
{};
struct Bird
: Animal
{};
int main() {
Dog d;
Animal& animal=d;
std::cout << typeid(animal).name() << std::endl; //Dog
return 0;
}
Как другие написали, лучше не полагаться на name()
. Но без какого-либо кода нелегко сказать, что правильно.
Ответ 5
Внедрить имя функции() в каждом подклассе.
Ответ 6
Поскольку список может содержать любой тип животного, я собираюсь предположить, что это список указателей. В этом случае typeid рассмотрит наиболее производный тип объекта, если вы передадите ему указатель с разнесением.
typeid(*animal).name();
Это то, что вы ищете.
Ответ 7
Без использования специальных трюков, чтобы предоставить информацию базового класса о производных типах, нет способа узнать, какой подтип является экземпляром. Самый простой способ сделать это, как предлагает @Joachim Wuttke, создать виртуальную функцию, которая вынуждает производные классы внедрять метод name().
Однако, если вы хотите немного поучаствовать, любопытно повторяющийся шаблон parttern CRTP предлагает более элегантное, если эзотерическое решение:
#include <typeinfo>
#include <string>
#include <iostream>
template <class T>
class Animal {
public:
virtual ~Animal() {}; // a base class
std::string name() {
return typeid(T).name();
}
};
class Cat: public Animal<Cat> {
};
class Dog: public Animal<Dog> {
};
int main( int argc, char* argv[] ){
Cat c;
Dog d;
std::cout << c.name() << std::endl;
std::cout << d.name() << std::endl;
}
result (g++):
3Cat
3Dog
result (vs2008):
class Cat
class Dog
Обратите внимание, что, как утверждают другие, mangling типа typeid зависит от платформы/компилятора, поэтому, чтобы перейти от имен выше к классу, вам нужно реализовать подпрограмму demandling, зависящую от платформы/компилятора. Не особенно сложно, но это отвлекает от элегантности решения.