C++类型判断
一、编译期类型判断(静态类型检查)
这类判断在编译阶段完成,零运行时开销,主要用于模板编程、类型萃取等场景。
1. typeid 运算符(基础)
typeid 可以获取类型信息,返回 std::type_info 对象,常用于判断两个类型是否相同。
代码示例:
#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_same、std::is_pointer、std::is_class 等模板,在编译期精准判断类型属性。
代码示例:
#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异常。
代码示例:
#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 会解析对象的实际类型(而非编译期类型):
#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;
}
三、常见使用场景
- 模板编程:用
std::is_same/std::is_pointer等编译期判断,实现类型分支逻辑。 - 多态场景:用
dynamic_cast判断基类指针/引用实际指向的子类类型。 - 类型安全检查:避免错误的类型转换(如将非多态类强制转换)。
总结
- 编译期判断:用
std::is_same/std::is_pointer等模板(<type_traits>),零运行时开销,适合模板编程。 - 运行期判断:仅适用于多态类,用
dynamic_cast(推荐)或typeid,有轻微运行时开销。 - 核心注意:
dynamic_cast依赖虚函数表,仅对多态类有效;typeid对非多态类仅返回编译期类型。

浙公网安备 33010602011771号