一、基础场景:编译期类型判断(静态类型检查)
C++ 是静态类型语言,大部分类型判断可以在编译期完成,这也是最常用、效率最高的方式。
1. typeid 运算符(最直接的类型判断)
typeid 是 C++ 内置运算符,能返回表示类型信息的 std::type_info 对象,核心用于:
- 判断两个变量的类型是否相同
- 获取类型的名称(注意:名称格式因编译器而异,仅作参考)
完整代码示例:
cpp
运行
#include <iostream>
#include <typeinfo> // 必须包含此头文件
int main() {
// 基础类型判断
int a = 10;
double b = 3.14;
std::string c = "test";
// 1. 判断类型是否相同
if (typeid(a) == typeid(int)) {
std::cout << "a 是 int 类型" << std::endl;
}
if (typeid(b) != typeid(a)) {
std::cout << "b 和 a 类型不同" << std::endl;
}
// 2. 获取类型名称(编译器相关,如GCC显示"i"代表int,MSVC显示"int")
std::cout << "a 的类型名:" << typeid(a).name() << std::endl;
std::cout << "c 的类型名:" << typeid(c).name() << std::endl;
// 3. 多态场景(注意:需是虚函数类,否则只识别静态类型)
class Base { virtual void func() {} };
class Derived : public Base {};
Base* ptr = new Derived();
std::cout << "ptr 指向的实际类型:" << typeid(*ptr).name() << std::endl; // 输出Derived
delete ptr;
return 0;
}
关键说明:
typeid对非多态类型(无虚函数的类)只会返回变量的静态类型(声明类型);- 对多态类型(有虚函数的类)会返回对象的实际类型(动态类型);
- 类型名称(
name())是编译器自定义的,比如 GCC 中int显示为i,std::string显示为Ss,仅用于调试,不要用于逻辑判断。
2. 模板特化 /std::is_same(编译期精确判断)
如果需要在编译期精准判断类型(比如写通用模板函数时),推荐用 C++11 引入的 std::is_same(头文件 <type_traits>),效率更高且更可靠。
完整代码示例:
cpp
运行
#include <iostream>
#include <type_traits> // 核心头文件
// 通用模板函数
template <typename T>
void checkType(T value) {
if constexpr (std::is_same_v<T, int>) { // C++17的is_same_v,等价于is_same<T,int>::value
std::cout << "类型:int,值:" << value << std::endl;
} else if constexpr (std::is_same_v<T, double>) {
std::cout << "类型:double,值:" << value << std::endl;
} else if constexpr (std::is_same_v<T, std::string>) {
std::cout << "类型:string,值:" << value << std::endl;
} else {
std::cout << "未知类型" << std::endl;
}
}
int main() {
checkType(10); // 输出:类型:int,值:10
checkType(3.14); // 输出:类型:double,值:3.14
checkType(std::string("hello")); // 输出:类型:string,值:hello
// 额外:判断是否为某类的子类(编译期)
class Base {};
class Derived : public Base {};
std::cout << "Derived是否是Base的子类:" << std::is_base_of_v<Base, Derived> << std::endl; // 输出1(true)
return 0;
}
关键说明:
std::is_same<T1, T2>::value返回布尔值,判断 T1 和 T2 是否是完全相同的类型;if constexpr(C++17)确保编译期就剔除不匹配的分支,无运行时开销;- 配套工具:
std::is_int(是否整型)、std::is_float(是否浮点型)、std::is_pointer(是否指针)等,满足各类基础类型判断。
二、进阶场景:运行期类型判断(动态类型检查)
主要用于多态场景(父类指针 / 引用指向子类对象),核心用 dynamic_cast。
完整代码示例:
cpp
运行
#include <iostream>
class Base {
public:
virtual ~Base() {} // 必须有虚函数,否则dynamic_cast无法使用
};
class Derived1 : public Base {
public:
void derived1Func() { std::cout << "这是Derived1类型" << std::endl; }
};
class Derived2 : public Base {
public:
void derived2Func() { std::cout << "这是Derived2类型" << std::endl; }
};
int main() {
Base* ptr = new Derived1();
// 1. dynamic_cast判断类型(指针版:失败返回nullptr)
if (Derived1* d1 = dynamic_cast<Derived1*>(ptr)) {
d1->derived1Func(); // 成功,调用子类方法
} else if (Derived2* d2 = dynamic_cast<Derived2*>(ptr)) {
d2->derived2Func();
}
// 2. 引用版:失败抛出std::bad_cast异常
try {
Derived1& ref = dynamic_cast<Derived1&>(*ptr);
ref.derived1Func();
} catch (const std::bad_cast& e) {
std::cout << "类型转换失败:" << e.what() << std::endl;
}
delete ptr;
return 0;
}
关键说明:
dynamic_cast仅适用于有虚函数的类(多态类型);- 指针类型转换失败返回
nullptr,引用类型转换失败抛出std::bad_cast异常; - 运行期类型判断有一定性能开销,非必要不使用(优先用多态直接调用虚函数)。
三、避坑提醒
- 不要依赖
typeid().name()的返回值做逻辑判断(编译器差异大); - 编译期判断(
std::is_same)优先于运行期判断(dynamic_cast/typeid),效率更高; dynamic_cast必须有虚函数,否则编译报错;- 模板场景下,优先用编译期类型判断(模板特化 /
type_traits),避免运行时开销。
总结
- 编译期类型判断 :用
std::is_same/std::is_base_of等(<type_traits>),无运行时开销,适合模板、基础类型判断; - 运行期类型判断 :多态场景用
dynamic_cast(最可靠),简单类型对比用typeid(注意多态 / 非多态区别); - 优先使用编译期判断,仅在多态动态类型识别时用运行期判断。