C++类型判断

一、编译期类型判断(静态类型检查)

这类判断在编译阶段完成,零运行时开销,主要用于模板编程、类型萃取等场景。

1. typeid 运算符(基础)

typeid 可以获取类型信息,返回 std::type_info 对象,常用于判断两个类型是否相同。

代码示例

cpp

运行

复制代码
#include <iostream>
#include <typeinfo> // 必须包含此头文件

class Base {};
class Derived : public Base {};

int main() {
    // 1. 判断基本类型
    int a = 10;
    double b = 3.14;
    std::cout << "type of a: " << typeid(a).name() << std::endl; // 输出:int(不同编译器可能简写,如i)
    std::cout << "type of b: " << typeid(b).name() << std::endl; // 输出:double(如d)

    // 2. 判断自定义类型
    Base base;
    Derived derived;
    std::cout << "base type: " << typeid(base).name() << std::endl;    // Base
    std::cout << "derived type: " << typeid(derived).name() << std::endl; // Derived

    // 3. 判断类型是否相同
    if (typeid(a) == typeid(int)) {
        std::cout << "a is int type" << std::endl;
    }

    // 注意:非多态类型,指针/引用不会触发动态类型判断
    Base& ref = derived;
    std::cout << "ref type: " << typeid(ref).name() << std::endl; // 输出Base(非多态)

    return 0;
}

关键说明

  • typeid 返回的 name() 结果是编译器相关的(比如 GCC 会简写类型名),不能直接依赖字符串内容判断类型。
  • 对于非多态类型 (类中无虚函数),typeid 仅判断编译期类型,不会解析实际指向的对象类型。
2. 模板类型萃取(C++11 及以上)

通过标准库的 std::is_samestd::is_pointerstd::is_class 等模板,在编译期精准判断类型属性。

代码示例

cpp

运行

复制代码
#include <iostream>
#include <type_traits> // 必须包含此头文件

template <typename T>
void check_type(T value) {
    // 判断是否为指定类型
    if constexpr (std::is_same_v<T, int>) { // C++17的is_same_v,等价于std::is_same<T, int>::value
        std::cout << "Type is int" << std::endl;
    } else if constexpr (std::is_same_v<T, double>) {
        std::cout << "Type is double" << std::endl;
    }

    // 判断类型属性
    std::cout << "Is pointer: " << std::boolalpha << std::is_pointer_v<T> << std::endl;
    std::cout << "Is floating point: " << std::is_floating_point_v<T> << std::endl;
    std::cout << "Is arithmetic: " << std::is_arithmetic_v<T> << std::endl; // 算术类型(int/float等)
}

int main() {
    check_type(10);        // int,非指针,非浮点,算术类型
    check_type(3.14);      // double,非指针,浮点,算术类型
    check_type(&10);       // int*,指针,非浮点,非算术类型

    // 自定义类型判断
    class MyClass {};
    std::cout << "Is class: " << std::is_class_v<MyClass> << std::endl; // true

    return 0;
}

关键说明

  • std::is_same<T, U>:判断 T 和 U 是否是完全相同的类型(包括 const/volatile 修饰)。
  • if constexpr(C++17):编译期条件判断,不会为不满足的分支生成代码,避免运行时开销。
  • 常用类型萃取模板:std::is_pointer(指针)、std::is_reference(引用)、std::is_const(常量)、std::is_base_of(基类判断)。

二、运行期类型判断(动态类型检查)

适用于多态类(含虚函数),在运行时判断对象的实际类型,核心是 dynamic_cast

1. dynamic_cast 类型转换(核心)

dynamic_cast 用于多态类型的向下转换(子类→基类)或交叉转换,转换失败时:

  • 指针类型:返回 nullptr
  • 引用类型:抛出 std::bad_cast 异常。

代码示例

cpp

运行

复制代码
#include <iostream>
#include <typeinfo>
#include <exception>

// 多态基类(必须有虚函数)
class Base {
public:
    virtual ~Base() = default; // 虚析构函数,使类成为多态
};

class Derived1 : public Base {};
class Derived2 : public Base {};

void check_dynamic_type(Base* ptr) {
    // 判断是否为Derived1类型
    if (Derived1* d1_ptr = dynamic_cast<Derived1*>(ptr)) {
        std::cout << "Object is Derived1 type" << std::endl;
    }
    // 判断是否为Derived2类型
    else if (Derived2* d2_ptr = dynamic_cast<Derived2*>(ptr)) {
        std::cout << "Object is Derived2 type" << std::endl;
    }
    else {
        std::cout << "Unknown type" << std::endl;
    }
}

int main() {
    Base* b1 = new Derived1();
    Base* b2 = new Derived2();
    Base* b3 = new Base();

    check_dynamic_type(b1); // Derived1
    check_dynamic_type(b2); // Derived2
    check_dynamic_type(b3); // Unknown type

    // 引用类型的dynamic_cast(失败抛异常)
    try {
        Base& ref = *b2;
        Derived1& d1_ref = dynamic_cast<Derived1&>(ref);
    } catch (const std::bad_cast& e) {
        std::cout << "Cast failed: " << e.what() << std::endl;
    }

    delete b1; delete b2; delete b3;
    return 0;
}

关键说明

  • dynamic_cast 仅对多态类(含虚函数)有效,否则编译报错。
  • 运行时开销:dynamic_cast 会查询类型信息表(vtable),有轻微运行时开销,应避免频繁使用。
2. 结合 typeid 实现动态类型判断

对于多态类,typeid 会解析对象的实际类型(而非编译期类型):

cpp

运行

复制代码
#include <iostream>
#include <typeinfo>

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

int main() {
    Base* ptr = new Derived();
    std::cout << typeid(*ptr).name() << std::endl; // Derived(实际类型)
    delete ptr;
    return 0;
}

三、常见使用场景

  1. 模板编程 :用 std::is_same/std::is_pointer 等编译期判断,实现类型分支逻辑。
  2. 多态场景 :用 dynamic_cast 判断基类指针 / 引用实际指向的子类类型。
  3. 类型安全检查:避免错误的类型转换(如将非多态类强制转换)。

总结

  1. 编译期判断 :用 std::is_same/std::is_pointer 等模板(<type_traits>),零运行时开销,适合模板编程。
  2. 运行期判断 :仅适用于多态类,用 dynamic_cast(推荐)或 typeid,有轻微运行时开销。
  3. 核心注意dynamic_cast 依赖虚函数表,仅对多态类有效;typeid 对非多态类仅返回编译期类型。
相关推荐
黎雁·泠崖2 小时前
整数的N进制字符串表示【递归+循环双版满分实现】
c语言·开发语言
张张努力变强2 小时前
C++类和对象(一):inline函数、nullptr、类的定义深度解析
开发语言·前端·jvm·数据结构·c++·算法
独自破碎E2 小时前
Java的CMS垃圾回收流程
java·开发语言
oioihoii2 小时前
C++线程编程模型演进:从Pthread到jthread的技术革命
java·开发语言·c++
2501_941322032 小时前
道路检测新突破:Cascade R-CNN在COCO数据集上的实战应用详解
开发语言·r语言·cnn
且去填词2 小时前
深入理解 GMP 模型:Go 高并发的基石
开发语言·后端·学习·算法·面试·golang·go
哪有时间简史2 小时前
Python程序设计基础
开发语言·python
zh_xuan3 小时前
kotlin对集合数据的操作
开发语言·kotlin
a程序小傲3 小时前
京东Java面试被问:多活数据中心的流量调度和数据同步
java·开发语言·面试·职场和发展·golang·边缘计算