概述
1. typeid关键字的引入
回顾c++的赋值兼容性原则,子类的对象在使用上可以被当做基类的对象,反过来则不可。具体表现在:
(1) 子类的对象可以被赋值给基本对象
(2) 指向基类的指针可以指向子类
(3) 子类的对象可以初始化基类对象的引用
class BaseCls
{
public:
//...
virtual ~BaseCls()
{}
};
class SubCls : public BaseCls
{
public:
//...
};
void test_func(BaseCls* p)
{
//将指针p强制转换为SubCls类型的指针
SubCls *ps = static_cast<SubCls* >(p);
}
int main(void)
{
BaseCls *pb = new SubCls();
test_func(pb);
delete pb;
return 0;
}
如上test_func()函数,在该函数的实现中,将基类类型p指针强制转换为子类类型指针,这是一种极为危险的操作:
因为BaseCls类型的p指针既可以接收BaseCls类对象的地址,还可以接收SubCls对象的地址,子类是从父类继承而来,子类包含了父类的所有成员,但是将父类强制转为子类,那就意味着转换后的“子类”将含有一部分未知的成员。
基类指针能否强制类型转换为子类指针取决于基类指向的类型,因为这个类型是在编译期间不能确定的,所以也称为动态类型。
要让test_func()函数中的强制类型转换是安全的,也就是要判断动态类型为子类指针后才进行强制类型转换,如何判断?根据前面所学的多态可以实现:
class BaseCls
{
public:
virtual std::string get_type()
{
return "BaseCls";
}
virtual ~BaseCls()
{}
};
class SubCls : public BaseCls
{
public:
virtual std::string get_type()
{
return "SubCls";
}
void print()
{
printf("Hello, SubCls!!n");
}
};
void test_func(BaseCls* p)
{
if (p->get_type() == "SubCls")
{
SubCls* ps = static_cast<SubCls* >(p);
ps->print();
}
else
{
printf("transcription error!!n");
}
}
int main(void)
{
SubCls *ps = new SubCls();
BaseCls *pb = new BaseCls();
test_func(ps); //打印Hello, SubCls!!
test_func(pb); //打印transcription error!!
delete ps;
delete pb;
return 0;
}
编译运行:
利用类的多态,看似完美解决地实现识别动态类型功能,但是存在问题
(1) 基类必须提供返回类型的虚函数,继承类必须重写基类的虚函数,这显然有点别扭
(2) 在实际开发中,继承类可能是由他人写的,他人有可能遗忘重写基类返回类型的虚函数,那么将无法实现动态类型识别
动态类型识别是一个重要功能,c++自然提供了关键字来实现,即typeid:
#include <typeinfo>
//...
void test_func(BaseCls* p)
{
const std::type_info& type = typeid(*p);
printf("type.name = %sn", type.name());
}
编译运行:
需要注意,不同的编译器返回的.name()可能会不一样。
2. typeid使用分析
typeid()关键字返回对应参数的类型信息,类型信息用type_info类对象存储:
const std::type_info& type = typeid(*p);
typeid关键字的返回值为const std::type_info的引用,type_info原型如下:
//摘自Linux系统的/usr/include/c++/4.5/typeinfo
class type_info
{
public:
virtual ~type_info(); //析构函数
const char* name() const
{
return __name[0] == '*' ? __name + 1 : __name;
}
#if !__GXX_TYPEINFO_EQUALITY_INLINE
bool before(const type_info& __arg) const;
bool operator==(const type_info& __arg) const;
#else
#if !__GXX_MERGED_TYPEINFO_NAMES
bool before(const type_info& __arg) const
{
return (__name[0] == '*' && __arg.__name[0] == '*')
? __name < __arg.__name
: __builtin_strcmp (__name, __arg.__name) < 0;
}
bool operator==(const type_info& __arg) const
{
return ((__name == __arg.__name)
|| (__name[0] != '*' &&
__builtin_strcmp (__name, __arg.__name) == 0));
}
#else
bool before(const type_info& __arg) const
{
return __name < __arg.__name;
}
bool operator==(const type_info& __arg) const
{
return __name == __arg.__name;
}
#endif
#endif
bool operator!=(const type_info& __arg) const
{
return !operator==(__arg);
}
virtual bool __is_pointer_p() const;
virtual bool __is_function_p() const;
virtual bool __do_catch(const type_info *__thr_type, void **__thr_obj,
unsigned __outer) const;
virtual bool __do_upcast(const __cxxabiv1::__class_type_info *__target,
void **__obj_ptr) const;
protected:
const char *__name;
explicit type_info(const char *__n): __name(__n) { } //explicit用于杜绝隐式类型转换,构造函数
private:
//私有化赋值操作符和拷贝构造函数
type_info& operator=(const type_info&);
type_info(const type_info&);
};
可见:
(1) 公有成员函数都是const属性的,只有const对象才能使用const属性的成员方法,这也就是为什么typeid()关键字返回的type_info是const的原因
(2) 看到type_info的拷贝构造函数和赋值操作符都是私有的,这也就意味着type_info对象不能初始化、赋值另一个type_info对象
(3) 构造函数是protected属性,即type_info不能在外部实例化(只能在继承类的内部实例化)
typeid()的参数可以是类型名,还可以是变量:
int a = 6;
const type_info& t1 = typeid(a);
printf("type.name = %sn", t1.name());
const type_info& t2 = typeid(int);
printf("type.name = %sn", t2.name());
double d = 0.63;
const type_info& t3 = typeid(d);
printf("type.name = %sn", t3.name());
const type_info& t4 = typeid("hello");
printf("type.name = %sn", t4.name());
编译运行:
当typeid()的参数为类型名时,返回的是静态类型信息。
当typeid()的参数为变量时,分两种情况:
(1) 变量不存在虚函数表,返回的是静态类型信息
(2) 变量存在虚函数表,返回动态类型信息
注意,同样的代码,在windows上的visual studio2010编译运行:
跟Linux上g++的打印结果是不一样的,所以在利用typeid()判断数据类型的时候,要以两个typeid()的返回值来判断,以int类型为例:
int a;
if (typeid(int) == typeid(a))
printf("type is int for an");
所以在一开始的动态类型识别函数test_func()的实现为:
void test_func(BaseCls* p)
{
const std::type_info& type = typeid(*p);
if (typeid(*p) == typeid(SubCls))
{
printf("进行强制类型n");
SubCls* ps = static_cast<SubCls* >(p);
//...
}
else
printf("不能进行强制类型转换n");
}
//...
int main(void)
{
SubCls *ps = new SubCls();
BaseCls *pb = new BaseCls();
test_func(ps);
test_func(pb);
delete ps;
delete pb;
return 0;
}
编译运行:
最后
以上就是迷人柚子为你收集整理的c++的typeid关键字的全部内容,希望文章能够帮你解决c++的typeid关键字所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复