一、type_info类
该类type_info
保存有关类型的特定于实现的信息,包括类型的名称以及比较两种类型是否相等或排序顺序的方式。这是typeid运算符返回的类。
#ifndef _TYPEINFO
#define _TYPEINFO
#include <exception>
namespace std
{
class type_info
{
public:
virtual ~type_info();
{ return __name[0] == '*' ? __name + 1 : __name; }
bool before(const type_info& __arg) const
{ return __name < __arg.__name; }
bool operator==(const type_info& __arg) const
{ return __name == __arg.__name; }
bool operator!=(const type_info& __arg) const
{ return !operator==(__arg); }
virtual bool __is_pointer_p() const;
virtual bool __is_function_p() const;
protected:
const char *__name;
explicit type_info(const char *__n): __name(__n) { }
private:
type_info& operator=(const type_info&);
type_info(const type_info&);
};
} // extern "C++"
#endif
type_info类提供了public 虚析构函数,以使用户能够用其作为基类。它的默认构造函数和拷贝构造函数及赋值操作符都定义为private,所以不能定义或复制type_info类型的对象。
程序中创建type_info对象的唯一方法是使用typeid操作符(由此可见,如果把typeid看作函数的话,其应该是type_info的 友元)。如果想使用type_info的引用,可以使用const type_info& info = typeid(foo)。
type_info::name返回的是类似于"class CBase*"之类的名字, 其用来表示相应的类型名,但务必注意这个返回的类型名与程序中使用的相应类型名并不一定一致,这具体由编译器的实现所决定的,标准只要求实现为每个类型返回唯一的字符。
对于父类指针类型变量,typeid(ptr).name不具有多态性, 仍返回此父类的指针类名,如"class CBase*"。如果直接传入对象如typeid(*ptr).name则具有多态性,将返回"class CDevievd"类似的子类类名。
下面演示一个使用typeid.name查询类名的示例:
class CDataType
{
public:
virtual char* class_name() { return "CDataType"; }
};
// 数据
class CDataType {};
class CDataTypeA : public CDataType {};
class CDataTypeB : public CDataType {};
CreatorType* GetCreator( CDataType* data )
{
return nameMap[typeid(*data).name()];
}
void main()
{
RegistCreator( "CDataTypeA", new CreatorForDataA );
RegistCreator( "CDataTypeB", new CreatorForDataB );
CDataTypeA* dataA = new CDataTypeA;
CreatorType* creator = GetCreator(dataA);
}
有的同事担心大量的字符串比较会影响速度,虽然map是很高效的。担心确实是不必要的,杀手锏还在你的手中——hash_code。
hash_code是把字符串映射到一个唯一整数,使用整数作为map键值效率要比string高许多。废话不说,上代码。
typedef int KeyType;
static std::map<KeyType, CreatorType*> nameMap;
void RegistCreator(KeyType key, CreatorType* creator)
{
nameMap[key] = creator;
}
CreatorType* GetCreator( CDataType* data )
{
return nameMap[typeid(*data).hash_code()];
}
void main()
{
RegistCreator( typeid(CDataTypeA).hash_code(), new CreatorForDataA );
RegistCreator( typeid(CDataTypeB).hash_code(), new CreatorForDataB );
CDataTypeA* dataA = new CDataTypeA;
CreatorType* creator = GetCreator(dataA);
}
二、typeid的使用
表达式:
typeid(type-id)
typeid(expression)
Typeid运算符允许在运行时确定对象的类型。ISO C++标准并没有确切定义type_info
,它的确切定义编译器相关的,但是标准却规定了其实现必需提供如下四种操作。
运算 | 描述 |
t1 == t2 | 如果两个对象t1和t2类型相同,则返回true;否则返回false |
t1 != t2 | 如果两个对象t1和t2类型不同,则返回true;否则返回false |
t.name() | 返回类型的C-style字符串,类型名字用系统相关的方法产生1 |
t1.before(t2) | 返回指出t1是否出现在t2之前的bool值 |
Typeid的结果是 const type_info&
。即一个type_info类(用于描述数据类型的一个系统类)对象的引用。这个操作符可以用于表达式和类型名(包括自定的数据类型,比如类) 具体取决于所使用的typeid形式。 有关详细信息,请参阅Type_info 类。
Typeid运算符不适用于托管类型(抽象声明符或实例)。 有关获取指定类型的 @no__t 的信息,请参阅typeid。
当应用于多态类类型的左值时, typeid运算符执行运行时检查,其中对象的 true 类型无法由提供的静态信息确定。 此类情况是:
-
对类的引用
-
一个指针,用
*
取消引用 -
下标指针(
[ ]
)。 (使用带有指向多态类型的指针的下标是不安全的。)
如果表达式指向基类类型,但该对象实际上是派生自该基类的类型,则结果是派生类的 @no__t 1 引用。 表达式必须指向多态类型(具有虚函数的类)。 否则,结果为表达式中引用的静态类的 type_info
。 此外,必须取消引用指针,以便使用的对象是它所指向的对象。 如果没有取消引用指针,结果将是指针的 type_info
,而不是它所指向的内容。 例如:
// expre_typeid_Operator.cpp
// compile with: /GR /EHsc
#include <iostream>
#include <typeinfo>
class Base {
public:
virtual void vvfunc() {}
};
class Derived : public Base {};
using namespace std;
int main() {
Derived* pd = new Derived;
Base* pb = pd;
cout << typeid( pb ).name() << endl; //prints "class Base *"
cout << typeid( *pb ).name() << endl; //prints "class Derived"
cout << typeid( pd ).name() << endl; //prints "class Derived *"
cout << typeid( *pd ).name() << endl; //prints "class Derived"
delete pd;
}
如果表达式取消引用指针,并且该指针的值为零,则typeid将引发bad_typeid 异常。 如果指针不指向有效的对象,则会引发 __non_rtti_object
异常。 它指示尝试分析因对象无效而触发了错误的 RTTI。 (例如,它是错误指针,或代码未使用/GR进行编译)。
如果该表达式不是指针,而不是对该对象的基类的引用,则结果为表示该表达式的静态类型的 type_info
引用。 表达式的静态类型引用表达式的类型,因为它在编译时是已知的。 在计算表达式的静态类型时,将忽略执行语义。 此外,在确定表达式的静态类型时,将忽略引用(如果可能):
// expre_typeid_Operator_2.cpp
#include <typeinfo>
int main()
{
typeid(int) == typeid(int&); // evaluates to true
}
还可以在模板中使用typeid来确定模板参数的类型:
// expre_typeid_Operator_3.cpp
// compile with: /c
#include <typeinfo>
template < typename T >
T max( T arg1, T arg2 ) {
cout << typeid( T ).name() << "s compared." << endl;
return ( arg1 > arg2 ? arg1 : arg2 );
}
三、 typeof 的使用
typeof由编译器提供(目前仅gcc编译器支持),返回一个字符串,表示未经计算的操作数的类型(某个变量或表达式的类型)。C++11标准新增的decltype是typeof的升级版本。
Typeof用于指定变量的数据类型,该关键字使用时的语法类似sizeof,但是其结构在语义上类似于用typedef定义的类型名称。
console.log(typeof 42);
// expected output: "number"
console.log(typeof 'blubber');
// expected output: "string"
console.log(typeof true);
// expected output: "boolean"
console.log(typeof declaredButUndefinedVariable);
// expected output: "undefined";
结果:
> "number"
> "string"
> "boolean"
> "undefined"
如果在一个被包含在ISO C程序中的头文件中使用该关键字,请使用__typeof__代替typeof。typeof可用在任何可使用typedef的地方,例如,可将它用在声明,sizeof或typeof内。
typeof在声明中与表达式结合非常有用,下面是二者结合用来定义一个获取最大值的“宏“:
#define max(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
本地变量使用下划线命名是为了避免与表达式中的变量名a和b冲突。
下面是一些typeof使用示例:
- 声明y为x指向的数据类型
typeof(*x) y;
- 声明y为x指向的数据类型的数组
typeof(*x) y[4];
- 声明y为字符指针数组
typeof(typeof(char *)[4]) y;
上述声明等价于 char *y[4];
Typeof在linux内中使用广泛,比如,宏container_of用于获取包含member的结构体type的地址:
/**
* container_of - cast a member of a structure out to the containing structure
*
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
typeof与typeid两者之间的主要区别如下
- typeof是一个编译时构造,并返回编译时定义的类型
- typeid是运行时构造,因此提供有关值的运行时类型的信息。
typeof参考:http : //www.delorie.com/gnu/docs/gcc/gcc_36.html
typeid参考:https : //en.wikipedia.org/wiki/Typeid
参考文章:
https://docs.microsoft.com/zh-cn/cpp/cpp/typeid-operator?view=vs-2019
https://blog.csdn.net/gatieme/article/details/50947821
https://www.cnblogs.com/dario/archive/2012/03/16/2399864.html