文章

类型检查

类型检查

[TOC]

typeid

在C++中,typeid是一个操作符,用于获取一个对象或类型表达式的类型信息。当对一个类型表达式使用typeid时,它返回一个std::type_info类的对象的引用。这个对象包含了有关表达式类型的信息,如类型的名称。typeid操作符是在头文件<typeinfo>中定义的。1

typeid的使用场景通常涉及到以下两种情况:

  1. 获取类型的类型信息:当对一个类型名称使用typeid时,你可以得到该类型的信息,不论这个类型是否是多态类型。

    1
    
    typeid(int) // 获取int类型的信息
    
  2. 获取对象的实际类型信息:当对对象表达式使用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

参考2

本文由作者按照 CC BY 4.0 进行授权