知道C++中一个子类的类
Know the class of a subclass in C++
我至少有7年没有做C++了,突然陷入了一个C++项目。我想要一些以下方面的指导:
我有一个名为Animal的类,我有三个从Animal继承的类:猫、狗和鸟。我已经创建了一个列表对象,并且正在使用它来存储Animal类型。
这个列表可以包含猫、狗和鸟,当我迭代这个动物列表时,我想知道每个动物的直接类型(无论是猫、狗还是鸟)。
当我说typeid(animal).name();
时,它给了我动物,这是真的,但我想知道什么样的动物。
有什么想法吗??我应该使用enums吗??
您几乎肯定不想知道。你应该做的是宣布与这些动物互动的虚拟适当方法。
如果您需要对它们进行特定操作,您可以使用访问者模式来传递访问者对象,也可以在每个具体类中使用正确的数据。如果您坚持使用标记(我强调这是第三种选择-其他两种解决方案会让您的代码更干净),那么使用一个名为classname
的虚拟方法,它会返回类型标识符(无论是字符串、int还是其他形式)。
如果您有一个对象类型的数组,而不是指针类型,请注意关于切片的要点。如果你已经7年没有使用C++了,你可能没有意识到模板使用量的增长会让语言变得更好。看看boost之类的库,看看可以做些什么,以及模板化如何允许您编写类型推断的泛型代码。
Dog* dog = dynamic_cast<Dog*>(myAnimalPointer);
if (dog == NULL)
{
// This animal is not a dog.
}
在C++中,需要知道特定的具体对象类型通常是一种设计气味,所以我建议您不要尝试这样做。
相反,在Animal
中创建一个抽象(纯虚拟)界面,描述您希望动物拥有的功能。然后,您可以利用动态调度来调用该功能,甚至不需要知道对象的动态类型。如果需要,您可以始终在子类中创建专用的非虚拟帮助器函数。
还要注意,您需要在容器中存储Animal
sby(智能)指针,而不是按值存储。如果按值存储它们,则在插入列表时,它们都将被切片,从而丢失动态类型信息。
正如@Marcin所指出的,如果您确实需要在特定的子类上调用特定的方法,那么使用访问者模式进行双重调度可能是一种更好的方法。
根据具体的代码typeid
返回不同的内容。此外,name()
可以返回任何内容(包括使第一个字母大写或删除*),仅用于调试。现在我有几个不同的可能答案typeid(animal).name()
可以返回什么。
版本1animal
是一个类名:
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
是Animal
:的typedef
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()
。但是,如果没有一些代码,就不容易说出什么是正确的。
由于列表可以包含任何类型的动物,我假设它是一个指针列表。在这种情况下,如果您将取消引用的指针传递给typeid,那么typeid将考虑对象的最派生类型。
typeid(*animal).name();
就是你想要的。
在每个子类中实现一个函数name()。
如果不使用特殊技巧向基类提供有关派生类型的信息,它就无法知道实例的子类型。最简单的方法是,正如@Joachim Wuttke所建议的,创建一个虚拟函数,强制派生类实现name()方法。
然而,如果你想更喜欢,奇怪地重复出现的模板部分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;
}
结果(g++):
3Cat
3Dog
结果(vs2008):
class Cat
class Dog
请注意,正如其他人所说,typeid的名称篡改是依赖于平台/编译器的,因此要从上面的名称返回到类,您必须实现依赖于平台或编译器的去映射例程。不是特别困难,但它确实削弱了解决方案的优雅性。
- 将父类的子类的数据复制到具有相同父类的另一个类
- C++有没有办法强制重写一组方法,如果其中一个方法在子类中具有重写?
- 创建子类的多个实例,其中只有一个超类实例
- 创建一个函数,该函数使用模板创建类或子类的对象
- 我想要一个具有子函数的函数访问相同的命名函数,而不使用它取决于其子类的类
- 我们可以在没有新实例化的情况下声明一个抽象方法来返回抽象超类中的子类对象吗
- 是否可以使一个类成为两个不同层次结构的子类?
- 如何将抽象类的可访问性限制为另一个抽象类及其子类?
- 是否可以为所有子类设置一个实例?
- 模板子类的一个示例
- 我们如何有证据表明,声明虚拟函数的类是2个字节,其中一个不超过一个未声明,而在子类中
- QT子类UI表单显示一个空白窗口
- C++:从两个包含子类 (typedef) 的列表创建一个列表
- 我有一个为 cpp 中的某个类定义的模板函数 - 不是 hpp.可以由子类使用吗?
- 如何创建一个包含C 中的子类的类的链接列表
- 如何处理一个子类有方法,而另一个没有方法的子类?
- 如何从另一个类访问一个类的方法/变量,而不在c++中实例化它或子类
- 将QQuickItem指针的子类指定给另一个C++对象
- C++在子类中创建一个可用的方法
- 为什么Qt提供了一个类来对其进行子类化