C++里的四大强制类型转换(static_cast、dynamic_cast、const_cast和reinterpret_cast)和RTTI

本文介绍了C++中的四种强制类型转换方法:static_cast用于子类转父类、内置类型转换,const_cast用于取消常量属性,reinterpret_cast用于任意类型间的转换,而dynamic_cast用于运行时类型检查,处理父类指针与子类对象的转换。

1.static_cast<> ()

static_cast<> 属于编译时类型检查     可以将子类对象强制转换成父类对象,还有系统内置类型的强制类型转换

class Base
{
public:
	void show()
	{
		cout << "Base 的show函数调用";
		cout << endl;
	}
};
class Son :public Base
{
public:
	void show()
	{
		cout << "Son 的show函数调用";
		cout << endl;
	}
};

上面的Son继承Base类,首先来看一下将子类对象转换成父类对象

Son s1;
Base b1 = static_cast<Base>(s1);//将s1转换成base对象(子类转父类)
b1.show();//此处调用的是Base类的show函数
s1.show();

注意:这四个强制类型转换在使用的时候都需要加上();此处将b1强制类型转换成Base类对象(子类转父类),static_cast<>()只能将子类对象转化成父类对象,将父类对象转换成子类对象会报错。

static_cast<>()将void*与其他类型指针之间的转换:

	int i = 10;
	int* p = &i;
	void* v = static_cast<void*>(p);//int 转void*
	int* m = static_cast<int*>(v);//void*转int

此处说明一下void*类型的指针是无类型的指针,就是它可以指向任何类型的指针,如果将其他的指针转换成void*类型的指针,在使用的时候要记得转回去,不然会出错

static_cast<>()最常用于内置类型之间的转换

	int a = 100;
	float b = static_cast<float>(a);//int 转float
	double db = static_cast<double>(a);//int 转double
	char ch = static_cast<char>(a);//int 转char
	cout <<a<< endl;
	cout << b << endl;
	cout << db << endl;
	cout << ch << endl;

内置类型之间的相互转换都是可行的,这种写法和c风格强制类型转换基本一致

 下面来谈谈static_cast<>()不可以转换的情况:

 不能用于内置类型的指针的转换,int* ,double*,float*,等

2.const_cast<>()

2.const_cast<>()属于编译时类型检查 ,能够去掉表达式的常量属性;能够取消指针或者引用的const属性,使其可以被修改

const int m = 100;//现在m是不可修改的
int arc = const_cast<int>(m);//错误的写法

当我们这样写是错误的,系统会报错

所以这里只能传指针或者引用 ,下面开始测试:

	const int m = 100;//现在m是不可修改的
	const int* p = &m;
	int *p1 = const_cast<int*>(p);
	*p1 = 10;
	cout << *p1 << endl;
	cout << *p<< endl;
    cout << m<<endl;

我们将指针p转换成可以修改的p1,修改p1的值后,p的值也会随之改变,m的值依旧不变

 注意:如果原来是一个常量,你通过const_cast<>()将其变为可修改的并且改变其值,这是一种未定义的行为,容易带来风险,最好不要这样写。

3.reinterpret_cast<>()

属于编译时类型检查;可以把一些不相干的类型之间实现强制类型转换,转换比较随意

int i = 1;
int* p1 = reinterpret_cast<int*>(&i);//将地址转换成int*
char* p2 = reinterpret_cast<char*>(&i);

对于第二个用法,需要注意,这个地址里面存的是int,却把他转成了字符型指针,虽然编译器没有报错,但是实际在使用过程中不建议这样写。

可以进行两个无关自定义类型指针之间的转换:

class A{};
class B{};

先定义两个无关的类

A *a=new A;
B *b = reinterpret_cast<B*>(a);

 可以看到转换成功,但是在实际应用中不建议这样乱转,不仅没有意义,而且会导致程序出错

4.dynamic_cast<>()

dynamic_cast<>()运行时类型检查;将父类指针转换成子类指针,通过父类指针创建的对象只能调用重写父类的那个虚函数,不能调用自己的成员函数,因此需要通过dynamic_cast<>()来解决这个问题

class Human
{
public:
	virtual void eat() = 0;
	void sleep(){}
	virtual ~Human()
	{

	}
	
};

class Woman:public Human
{
public:
	void eat()
	{
		cout << "吃饭" << endl;
	}
	void func()
	{
		cout << "调用woman的fun()" << endl;
	}
};

class Man :public Human
{
public:
	void eat()
	{
		cout << "吃肉" << endl;
	}
	void fun(){
		cout << "fun()函数调用" << endl;
	}
};

先写一个父类Human ,其内有一个纯虚函数 virtual void eat()=0;这个函数分别在两个子类里面都进行了重写,子类里面也有自己的成员函数func(),fun;注意父类的析构函数必须写成虚函数,否则会出现父类指针无法释放子类对象的情况。

Human* hum = new Man;
hum->eat();
cout << typeid(hum).name() << endl;//返会类型
Man* m = dynamic_cast<Man*>(hum);//将父类指针转换成子类指针,转换失败则返回空指针
m->fun();//转换之后就可以调用子类的成员函数
cout << typeid(m).name() << endl;

此处结合运行时类型识别 typeid() 一起说明,首先通过父类指针创建一个子类对象。此处hum->eat()会调用Man的eat函数,typeid里面传入的就是当前想要识别的对象指针,再通过调用其成员函数就会得到相应的类型名称,在通过dynamic_cast<>() 将父类指针转换成子类指针就可以调用子类的成员函数,dynamic_cast<>()也可以将父类指针转换成子类对象,但是这个不安全

 dynamic_cast<>()如果转换失败会返回一个bad_cast

Human& q = *hum;
try
{
	Man ma = dynamic_cast<Man&>(q);
	cout << "父类引用转换成子类引用" << endl;
	
}
catch (bad_cast)//如果转换失败返回bad_cast
{
	cout << "转换失败" << endl;
}

const type_info& tp = typeid(*hum);//type_inf返回常量引用
cout << tp.name() << endl;

我们用try catch机制捕捉一下试试看

 此处是转换成功的,没有输出“转换失败”,typeinfo 返回常量引用,可以通过它的name()成员函数返回类型。下面举一个转换失败的例子

class A {
public:
	virtual void func() { cout << "Class A" << endl; }
private:
	int m_a;
};
class B : public A {
public:
	virtual void func(){ cout << "Class B" << endl; }
private:
	int m_b;
};

注意此处父类中没有纯虚函数,因此父类是可以创建对象的

	A* pa = new A();
	B* pb;
	pb = dynamic_cast<B*>(pa);  //向下转型失败
	if (pb == NULL) {
		cout << "转换失败" << endl;
	}
	else {
		cout << "转换成功" << endl;
		pb->func();
	}

此处会输出转换失败,对于此处有一种说法是:dynamic_cast 会在程序运行过程中遍历继承链,如果途中遇到了要转换的目标类型,那么就能够转换成功,如果直到继承链的顶点(最顶层的基类)还没有遇到要转换的目标类型,那么就转换失败,映射到此处,pa创建的A类对象就是继承链的顶部了,不能再向上遍历继承链,因此转换失败.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值