c++ -typeid operator

本文深入探讨了C++中typeid操作符的功能和用法,包括如何通过它获取对象的实际类型信息,以及在不同场景下如何使用。通过具体实例展示了如何在调试器中应用typeid操作符,并解释了其在处理基类指针或引用时的行为。

typeid is the one of the meager offering of runtime type information hierarchy functionality. what it offers is the ability to determine at runtime the real type of an object (a pointer is a special type of variable, which we will lsee shorty in this post )

 

 

 

what is the typeid operator used for? 

 

it is used in advanced programming developement, when building debugger for example, or it is used to deal with persistent object through a pointer or a reference to a base class.

 

The program needs to find out the actual type of the object manipulated to list the properties of an object properly during a debugging session or to store and retrieve an object property 

 

During a debugging session or to store and retrieve an object property properly to find out the actual type of an object, the typeid operator can be used. 

 

 

For better describe what we will describe, here is the class hiearchy that we will use in this example. 

 

 

class employee 
{
};
class manager : public employee
{};

class programmer : public employee 
{
};

 

so, in the simplest case, what is the output of the followiong. 

 

 

void typeid_test() { 
	programmer obj ;
	programmer &re = obj;

	// we look at name() in the subsection on type_info 
	// below, name() returns the C-style string: "programmer"
	cout << typeid(re).name() << endl;
}

 

the typeid can accept two types of operand, one is an expression and the other is the name of the type. Let's first see the first type of operand (the later case will be covered in example later) . 

 

/**
* the operand of the typeid operator can be expression or type names of any type 
*
*/
void typeid_operand()
{
  int iobj;
  cout << typeid(iobj).name() << endl; // print : ints
  cout <<typeid(8.16).name() << endl; // prints : double
}

 when the oeprand to typeid operator is a class, but not a class of virtual function, the result of the typeid operator is well, not the type of underlying object.

 

 

/**
* if the typeid operand is a class type, but not of a class type with virtual functions. then the typeid operator indicates the type of 
* the operand is well, not the type of the underlying object
*/

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

void typeid_non_virtual_class() 
{
	Derived dobj;
	Base *pb = &dobj;

	cout << typeid(*pb).name() << endl; // prints : Base
}

 but, if the class does have virtual function, then the real derived object will be retrieved and its true type will be returned. 

 

/*
*  if the typeid oeprand is a class type , where if class to be examined is of virutal functions, then the 
* real derived classes will be returned.
*/
void typeid_result_virtual_class() 
{
	employee *pe = new manager();
	employee &re = *pe; 
	if (typeid(*pe) == typeid(manager)) cout << "typeid(*pe) == typeid(manager)" << endl; // true 
	if (typeid(re) == typeid(employee)) cout << "typeid(re) == typeid(employee)" << endl; // true 

	if (typeid(&re) == typeid(employee *)) cout << "typeid(&re) == typeid(employee *)" << endl;  // true 
	if (typeid(&re) == typeid(manager *)) cout << "typeid(&re) == typeid(manager *)" << endl; // false
}

 

you may have seen already that there are some difference between the object and poiner, the pointer type itself is fixed, so to get the dynamic behavior of typeid, you'd better pass in the real object or reference (which will be treated as the data object)

 

 

type_info class

You may find the type_info class declaration as follow. actually the real implementation can vary from implemenation to implemenation. 

 

 

class type_info { 
private:
	type_info(const type_info&);
	type_info& operator = (const type_info &);
public:
	virtual ~type_info();

	int operator ==(const type_info&) const; 
	int operator!=(const type_info &) const;
	
	const char *name() const; 
};
 

the only way to construct the type_info object is to use the typeid operator. 

 

what is containd in the type_info class is implementation dependant. the name() const char * is the only member guaranteed to be provided by all C++ implementation , though it is possible that implementaion dependant may provide additional support. what can be added, basically any information that a compiler can provide about a type about a type can be added. 

 

  • a list of the class member functions. 
  • what the layout of an object of this clases type looks like in storage, that is , how the member and base subobjects are mapped
class extended_type_info  :public type_info {};
typedef extended_type_info eti;

void func(employee * p)
{
	// downcast from type_info * to extended_type_info*
	if (const eti * eti_p = dynamic_cast<const eti *>(&typeid(*p))) {
		// if dynamic_cast succeeds
		// use extended_type_info information through eti_p
	} else{ 
		// if dynamically fails
		// use standard type_info information
	}
}
 
<think>我们被要求解释C++中的typeid运算符和type_info类。根据引用资料,我们可以总结如下: 1. **typeid运算符**:用于在运行时获取对象的类型信息。它返回一个对`std::type_info`对象的引用。 2. **type_info类**:定义在头文件`<typeinfo>`中,它包含类型的相关信息。该类至少有一个`name()`成员函数,返回一个表示类型名称的字符串(具体字符串取决于编译器实现)。此外,`type_info`重载了`==`和`!=`运算符,用于比较两个类型是否相同。 需要注意的是: - 对于没有虚函数的类,`typeid`在编译时确定,返回的是静态类型(即声明类型)。 - 对于有虚函数的类(即多态类型),`typeid`会在运行时动态确定,返回实际指向的对象的类型(动态类型)。 - 每个类型的`type_info`对象在程序中是唯一的。无论使用`typeid`多少次,对于同一类型,返回的都是同一个`type_info`对象的引用。 根据引用[2]:如果没有虚函数,即使基类指针指向派生类,`typeid`返回的也是基类类型;如果有虚函数,则返回派生类类型(因为虚表的存在使得可以在运行时确定类型)。 引用[4]和[5]都提到:`typeid`返回的是`type_info`对象的引用,并且同一个类型的`type_info`对象是唯一的。 下面我们写一个详细的说明,包括基本用法、注意事项和示例代码。 ### C++typeid和type_info的详细说明 #### 1. 基本概念 - **typeid运算符**:用于获取表达式或类型的类型信息。语法形式有两种: - `typeid(type)`:获取指定类型的信息。 - `typeid(expression)`:获取表达式的类型信息(如果表达式是多态类型,则可能进行运行时类型识别)。 - **type_info类**:表示C++中的类型信息。它定义在标准头文件`<typeinfo>`中,通常由编译器实现。该类不能被直接实例化或复制,只能通过`typeid`运算符获取其引用。 #### 2. type_info类的主要成员函数 - `const char* name() const`:返回一个以空字符结尾的字符串,表示类型的名称。注意,返回的字符串是编译器相关的(可能经过名称修饰)。如果需要可读性更好的字符串,可以使用解名称修饰(demangle)函数(如GCC的`abi::__cxa_demangle`)。 - `bool operator==(const type_info& rhs) const`:比较两个`type_info`对象是否表示同一类型。 - `bool operator!=(const type_info& rhs) const`:比较两个`type_info`对象是否表示不同类型。 - `bool before(const type_info& rhs) const`:返回当前类型是否在排序上先于`rhs`类型(用于实现`type_index`的排序)。 #### 3. 使用注意事项 - **静态类型与动态类型**: - 对于非多态类型(即没有虚函数的类型),`typeid`在编译时确定,返回的是表达式的静态类型。 - 对于多态类型(即包含虚函数的类),`typeid`会对表达式求值,并在运行时确定其动态类型。 - **头文件**:使用`typeid`和`type_info`必须包含头文件`<typeinfo>`。 - **异常**:如果`typeid`的操作数是一个解引用空指针的多态类型,则会抛出`std::bad_typeid`异常。 #### 4. 示例代码 ```cpp #include <iostream> #include <typeinfo> class Base { public: virtual ~Base() {} // 使Base成为多态类型 }; class Derived : public Base {}; int main() { int i; double d; int* p; Base base; Derived derived; Base* pb = &derived; // 基类指针指向派生类对象 // 内置类型 std::cout << "int: " << typeid(int).name() << std::endl; std::cout << "double: " << typeid(d).name() << std::endl; std::cout << "int*: " << typeid(p).name() << std::endl; // 非多态类型:静态类型 std::cout << "Base (static): " << typeid(base).name() << std::endl; std::cout << "Derived (static): " << typeid(derived).name() << std::endl; // 多态类型:动态类型(通过基类指针) std::cout << "pb (dynamic): " << typeid(*pb).name() << std::endl; // 实际类型是Derived // 比较类型 if (typeid(i) == typeid(int)) std::cout << "i is int" << std::endl; if (typeid(pb) != typeid(Derived*)) std::cout << "pb is not Derived*" << std::endl; return 0; } ``` **输出示例(GCC)**: ``` int: i double: d int*: Pi Base (static): 4Base Derived (static): 7Derived pb (dynamic): 7Derived i is int pb is not Derived* ``` 注意:`name()`返回的字符串是编译器相关的,上述输出是在GCC下的结果(名称经过修饰)。如果需要可读的名称,可以使用解修饰函数(如前所述)。 #### 5. 在多态中的行为 引用[2]中明确指出:对于没有虚函数的基类指针,即使指向派生类,`typeid`返回的也是基类类型。对于有虚函数的类(多态类型),则返回实际派生类类型。 #### 6. 总结 - `typeid`和`type_info`提供了C++的运行时类型识别(RTTI)机制。 - 主要用于类型比较和获取类型名称。 - 在多态类型中,`typeid`可以获取动态类型;在非多态类型中,只能获取静态类型。 §§相关问题§§ 1. 如何在不同编译器下解修饰`type_info::name()`返回的字符串? 2. `typeid`在模板元编程中有哪些应用? 3. 如何通过`type_info`实现自定义的类型反射? 4. C++中的`std::bad_typeid`异常在什么情况下抛出? </think>### C++typeid 与 type_info 的详细说明 #### 1. 核心概念 - **typeid运算符**:C++ 的运行时类型识别(RTTI)机制的核心组件,用于获取表达式或类型的类型信息[^3] - **type_info类**:存储类型信息的类(定义在 `<typeinfo>` 头文件中),`typeid` 返回其对象的引用[^5] #### 2. 工作原理 1. **类型标识**: ```cpp const std::type_info& ti = typeid(expression); ``` 对于任意表达式,`typeid` 返回对应的 `type_info` 对象引用[^4] 2. **类型比较**: ```cpp if(typeid(a) == typeid(b)) { /* 类型相同 */ } ``` 使用重载的 `==` 和 `!=` 运算符进行类型比较[^5] 3. **动态类型识别**: ```cpp Base* ptr = new Derived(); typeid(*ptr); // 返回Derived类型(type_info) ``` 对有虚函数的类(多态类型),返回实际对象的动态类型[^2] #### 3. 关键特性 | 特性 | 说明 | 示例 | |------|------|------| | **唯一性** | 同一类型在程序中对应唯一 type_info 对象 | `&typeid(int) == &typeid(int)` 恒真[^4] | | **名称获取** | `name()` 成员函数返回类型名称字符串 | `typeid(int).name()` → "i"(GCC)[^5] | | **静态类型** | 无虚函数的类返回静态(声明)类型 | `Base* b = new Derived(); typeid(*b)` → `Base`[^2] | | **动态类型** | 有虚函数的类返回实际对象类型 | `Base* b = new Derived(); typeid(*b)` → `Derived`[^2] | #### 4. 使用示例 ```cpp #include <iostream> #include <typeinfo> #include <string> class Base { virtual void foo() {} }; // 多态基类 class Derived : public Base {}; int main() { int n = 10; double d = 3.14; std::string s = "text"; // 基本类型识别 std::cout << typeid(n).name() << "\n"; // 输出: i (int) std::cout << typeid(d).name() << "\n"; // 输出: d (double) std::cout << typeid(s).name() << "\n"; // 输出: NSt7__cxx1112basic_string... // 类型比较 if(typeid(n) == typeid(int)) std::cout << "n is int\n"; // 执行 // 多态类型识别 Base* b1 = new Base(); Base* b2 = new Derived(); std::cout << typeid(*b1).name() << "\n"; // 输出: 4Base std::cout << typeid(*b2).name() << "\n"; // 输出: 7Derived delete b1; delete b2; return 0; } ``` #### 5. 注意事项 1. **必须包含头文件**:`#include <typeinfo>` 2. **名称不可移植性**:`name()` 返回的字符串格式编译器相关[^5] 3. **解引用空指针**:对多态类型解引用空指针会抛出 `std::bad_typeid` 异常 4. **性能考量**:动态类型识别需要运行时开销,虚表查询约 $O(1)$ 时间[^2] 5. **模板类型**:返回完全实例化的具体类型: ```cpp std::vector<int> v; typeid(v).name(); // 返回类似 "St6vectorIiSaIiEE" ``` #### 6. 类型名称解混淆 使用 `abi::__cxa_demangle` (GCC/Clang) 获取可读名称: ```cpp #include <cxxabi.h> std::string demangle(const char* name) { int status = 0; char* demangled = abi::__cxa_demangle(name, 0, 0, &status); std::string result = (status == 0) ? demangled : name; free(demangled); return result; } // 使用: demangle(typeid(std::string).name()) → "std::string" ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值