本章主要内容:
一,动态类型转换
二,dynamic_cast运算符使用介绍
三,RTTI概念介绍
四,typeid运算符使用介绍
五,type_info类简介
六,参考阅读
C语言风格的强制类型转换不区分应用场景,C 中根据不同的应用场景提供了4种强制类型转换:
1.static_cast
静态类型转换,用来替代C语言风格的强制类型转换和隐式类型转换。
2.dynamic_cast
动态类型转换,应用在运行时的类型转换和识别,常用来将父类类型转换成子类类型。
3.const_cast
const类型转换,可以去除指针或引用的const属性,不能对常量使用const_cast。
4.reinterpret_cast
非关联类型之间的转换,不推荐使用。
一,动态类型转换
动态强制类型转换在代码运行期间进行,动态强制类型转换的实现需要使用dynamic_cast运算符。
dynamic_cast运算符只能应用于多态类相关的指针和应用,且使用dynamic_cast的类需要至少包含一个虚函数。
dynamic_cast运算符的使用方式与static_cast运算符的方式相同,如果强制类型转换不成功,指针会被设置为nullptr。
基类指针只允许调用派生类的虚函数,而dynamic_cast运算符生成的指针可以调用非虚函数。
dynamic_cast运算符在类型转换期间不会改变指针的const属性,如果需要强制类型转换的指针是const类型的,则目标指针也必须是const类型的。
动态类型转换的情况分两种:
1.downcast方式:沿着类层次结构,向下进行强制类型转换,从基类的指针转换为派生类的指针。
2.crosscast方式:相同层次的不同类之间的强制类型转换。
二,dynamic_cast运算符使用介绍
dynamic_cast应用于指针类型的代码样式:
代码语言:javascript复制<type> *p_subclass = dynamic_cast<<type> *>( p_obj );
dynamic_cast应用于引用类型的代码样式:
代码语言:javascript复制<type> subclass = dynamic_cast<<type> &>( ref_obj );
对比一下C语言中的强制类型转换的写法:
代码语言:javascript复制Human* phuman = new Men;
Men* p = (Men*)(phuman);
以上写法无法判断是否转换成功,而dynamic_cast运算符可以检查转换是否成功:
对于指针类型的转换,dynamic_cast运算符转换失败会返回一个空指针。
对于引用类型的转换,dynamic_cast运算符转换失败会抛出bad_cast异常。
完整代码用例如下:
代码语言:javascript复制#include <iostream>
#include <string>
#include <string_view>
class A
{
public:
virtual ~A()
{
}
};
class B : public A
{
};
int main()
{
A* p = new A;
//引用类型转换
try
{
B& b = dynamic_cast<B&>(*p);
}
catch (std::bad_cast exp)
{
std::cout << "Caught bad castn";
}
//指针类型转换
try
{
B* pB = dynamic_cast<B*>(p);
if (pB == NULL)
{
std::cout << "NULL Pointern";
}
}
catch (std::bad_cast exp)
{
std::cout << "Caught bad castn";
}
return 0;
}
运行结果:
代码语言:javascript复制Caught bad cast
NULL Pointer
三,RTTI概念介绍
RTTI的全称是"Run Time Type Identification",即运行时类型识别。
RTTI可以让程序借助基类的指针或引用去检查子类对象的类型。
RTTI的主要目的是获得子类对象的具体信息。
在RTTI场景中,父类的指针可以指向子类对象,代码样例如下:
代码语言:javascript复制#include <iostream>
using namespace std;
class A
{
public:
virtual void foo()
{
cout << "A's foo()" << endl;
}
};
class B : public A
{
public:
void foo()
{
cout << "B's foo()" << endl;
A::foo();
}
};
int main()
{
B bobj;
A* aptr = &bobj;
aptr->foo();
}
运行结果:
代码语言:javascript复制B's foo()
A's foo()
RTTI主要基于两个运算符来实现:dynamic_cast运算符 & typeid运算符。
1.dynamic_cast运算符: 以安全的方式将父类的指针或引用转换为派生类的指针或引用。
2.typeid运算符:返回指针或引用所指向对象的具体类型。
RTTI中的dynamic_cast运算符可以让父类对象调用子类对象中的普通成员函数。
RTTI中使用dynamic_cast运算符和typeid运算符的相同前提条件:父类中至少有一个虚函数。
四,typeid运算符使用介绍
typeid运算符的使用方式有两种:
代码语言:javascript复制typeid(类型名)
typeid(表达式)
typeid相等的条件:
(1).两个指针在定义时的类型相同,比如都是"ClassA *"类型,则它们的typeid相等。
(2).两个指针在运行时指向的类型相同,则它们的typeid相等。
代码样例:
代码语言:javascript复制Human* phuman = new Men;
if(typeid(*phuman) == typeid(Men))
{
cout << "phuman point to Men" << endl;
}
完整代码用例如下:
代码语言:javascript复制#include <iostream>
#include <typeinfo>
class B1 {
public:
virtual void fun() {}
};
class B2 {};
class D1 : public B1 {};
class D2 : public B2 {};
using namespace std;
int main() {
D1* d1 = new D1;
B1* b1 = d1;
D2* d2 = new D2;
B2* b2 = d2;
cout << typeid(d1).name() << endl;
cout << typeid(*d1).name() << endl;
cout << typeid(b1).name() << endl;
cout << typeid(*b1).name() << endl;
cout << typeid(d2).name() << endl;
cout << typeid(*d2).name() << endl;
cout << typeid(b2).name() << endl;
cout << typeid(*b2).name() << endl;
}
运行结果:
代码语言:javascript复制class D1 *
class D1
class B1 *
class D1
class D2 *
class D2
class B2 *
class B2
注意,使用typeid运算符可以返回一个type_info对象。
五,type_info类简介
type_info是一个类,用于描述编译器在程序运行期间生成的类型信息。
type_info类的对象可以存储指向类型的名称的指针。
type_info对象只能被typeid运算符构造,不能直接实例化type_info对象,也不能复制type_info类的值。
type_info类的常用成员函数有:
operator==:检查类型是否相等。
operator!=:检查类型是否不相等。
before:检查类型的排序。
name:返回类型名称。
hash_code:返回类型对应的标识符。
代码样例:
Demo1:
代码语言:javascript复制#include <iostream>
#include <typeinfo>
int main() {
int i;
int* pi;
std::cout << "int is: " << typeid(int).name() << 'n';
std::cout << " i is: " << typeid(i).name() << 'n';
std::cout << " pi is: " << typeid(pi).name() << 'n';
std::cout << "*pi is: " << typeid(*pi).name() << 'n';
return 0;
}
运行结果:
代码语言:javascript复制int is: int
i is: int
pi is: int *
*pi is: int
Demo2:
代码语言:javascript复制#include <iostream>
#include <typeinfo>
int main() {
if (typeid(int).before(typeid(char)))
std::cout << "int goes before char in this implementation.n";
else
std::cout << "char goes before int in this implementation.n";
return 0;
}
运行结果:
代码语言:javascript复制char goes before int in this implementation.
Demo3:
代码语言:javascript复制#include <iostream>
#include <typeinfo>
struct Base {};
struct Derived : Base {};
struct Poly_Base { virtual void Member() {} };
struct Poly_Derived : Poly_Base {};
typedef int my_int_type;
int main() {
std::cout << std::boolalpha;
// fundamental types:
std::cout << "int vs my_int_type: ";
std::cout << (typeid(int) == typeid(my_int_type)) << 'n';
// class types:
std::cout << "Base vs Derived: ";
std::cout << (typeid(Base) == typeid(Derived)) << 'n';
// non-polymorphic object:
Base* pbase = new Derived;
std::cout << "Base vs *pbase: ";
std::cout << (typeid(Base) == typeid(*pbase)) << 'n';
// polymorphic object:
Poly_Base* ppolybase = new Poly_Derived;
std::cout << "Poly_Base vs *ppolybase: ";
std::cout << (typeid(Poly_Base) == typeid(*ppolybase)) << 'n';
return 0;
}
运行结果:
代码语言:javascript复制int vs my_int_type: true
Base vs Derived: false
Base vs *pbase: true
Poly_Base vs *ppolybase: false
六,参考阅读
《C 新经典》
《C Primer》
《C Primer Plus》
《C 高级编程》
https://cplusplus.com/reference/typeinfo/type_info/