类型检查
[TOC]
typeid
在C++中,typeid是一个操作符,用于获取一个对象或类型表达式的类型信息。当对一个类型表达式使用typeid时,它返回一个std::type_info类的对象的引用。这个对象包含了有关表达式类型的信息,如类型的名称。typeid操作符是在头文件<typeinfo>中定义的。1
typeid的使用场景通常涉及到以下两种情况:
-
获取类型的类型信息:当对一个类型名称使用
typeid时,你可以得到该类型的信息,不论这个类型是否是多态类型。1
typeid(int) // 获取int类型的信息
-
获取对象的实际类型信息:当对对象表达式使用
typeid时,若表达式的类型是多态类型(即含有至少一个虚函数的类),typeid返回的将是对象动态类型(运行时类型)的信息,否则返回的是静态类型(编译时类型)。1 2 3 4 5 6 7 8 9
class Base { virtual void func() {} }; class Derived : public Base {}; Base* basePtr = new Derived(); std::cout << typeid(*basePtr).name(); // 会输出Derived类的类型信息
typeid在遇到对象表达式时以多态的方式工作:如果该类型是一个多态类型,typeid会提供对应于对象的动态类型的type_info对象;如果该类型不是多态类型,typeid则提供对应于对象的静态类型的type_info对象。换言之,对于多态类型,typeid会考虑对象的实际派生类型,而不仅仅是其声明类型。
type_info对象至少包含以下两个方法:
name():返回一个表示类型名称的字符串。operator==()和operator!=():用于比较两个类型是否相同。
需要注意的是,由于name方法返回的类型名称在不同编译器中可能不一样,也可能包含编译器特定的修饰符,因此它主要用于调试目的,而不是作为程序逻辑的一部分。在实际程序中,相比较于类型的名称,类型的唯一性比较(即使用type_info对象的operator==())通常更加有用。
此外,为了使typeid可以提供正确的运行时类型信息,对象表达式应该指向一个完整对象。如果对象因为某些原因(如对象被切片)而不完整,typeid可能无法返回正确的类型信息,并且可能导致未定义行为。
RTTI
RTTI是Run-Time Type Information的缩写,意为“运行时类型信息”。这是C++语言中的一个特性,它允许在程序运行时查询和操作对象的类型信息。1
RTTI提供了以下几种用途:
1. 类型识别
使用typeid操作符可以获取一个表达式的类型。typeid返回一个指向type_info对象的引用,该对象在程序的整个生命周期内保持不变。type_info类提供了用于比较两个类型是否相同的能力,以及获取类型名称的能力。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <typeinfo>
class Base { virtual void dummy() {} };
class Derived : public Base {};
Base* a = new Base;
Base* b = new Derived;
if (typeid(*a) == typeid(*b)) {
// 这里不会执行,因为a和b指向的对象类型不同
}
if (typeid(*b) == typeid(Derived)) {
// 这里会执行,因为b指向一个Derived类型的对象
}
2. 安全的向下转型
dynamic_cast操作符可以在类层次结构中安全地向下转换指针或引用。如果转换是有效的,那么dynamic_cast会返回转换后的指针或引用;如果转换无效,则根据被转换的是指针还是引用,返回nullptr或抛出std::bad_cast异常。
1
2
3
4
5
6
7
8
9
Base* b = new Derived;
// 安全向下转型
Derived* d = dynamic_cast<Derived*>(b);
if (d) {
// 转换成功,d现在指向Derived对象
} else {
// 转换失败,b不是指向Derived对象
}
使用RTTI需要在类中至少有一个虚函数,因为编译器使用虚函数表来存储类型信息。如果一个类没有任何虚函数,那么它不能支持RTTI。
需要注意的是,RTTI在某些项目中可能会因为性能考虑而被禁用,因为它会增加一些运行时开销和内存使用。此外,RTTI的使用也可能受限于项目的编码规范。在设计类层次结构时,应当根据具体需求考虑是否启用RTTI。
std::is_base_of
std::is_base_of 是C++标准库中的一个类型特征模板,用于在编译时检查一个类型是否是另一个类型的基类。这是编译时类型检查的一部分,常用于模板元编程和编写类型依赖的代码。
1
2
3
4
5
6
#include <type_traits>
class Base {};
class Derived : public Base {};
bool isBase = std::is_base_of<Base, Derived>::value; // isBase 将是 true