C++语法|C++的类型转换:const_cast、static_cast、reinterpret_cast、dynamic_cast

在C语言中,我们的类型强转都是:
int a = (int)b;

当然在C++中,提供了语言级别的四种类型转换方式:

  • const_cast:去掉常量属性的一个类型转换
  • static_cast:提供编译器认为安全的类型转换
  • reinterpret_cast:类似于C风格的强制类型转换
  • dynamic_cast: 主要用在继承结构中,可以支持RTTI类型识别的上下转换

const_cast

为去掉常量属性的一个类型转换:

const int a = 10;
//int *p = &a; //报错!!

在这里,我们肯定是不能拿普通之战指向一个常量类型的,
我们可以使用C风格的方式进行类型转换,我们也可以用C++风格的类型转换,想把它转成什么类型,尖括号<>就写什么类型

int *p1 = (int*)&a;
int *p2 = const_cast<int*>(&a);

其实这个类型都是语言级别提供的,所以我们无法查看它的源码。
但其实,他们在汇编基本的语法都是一样的:

lea	eax, |a|
mov	dword ptr [p1], eax

lea	eax, |a|
mov	dword ptr [p2], eax

但是在编译阶段,还是有所不同的:

  • const int a 在内存中是占4个字节的
  • 如果是C风格的类型转换,我们可以double *p1 = (double*)&a,double是8个字节的,但是仍然可以编译通过。
  • 如果我们想通过char *p2 = const_cast<char*>(&a), 用不同类型的指针指向一个整型变量,编译会直接报错。

这样可以消除可能的错误,比如说我们的 const int a 只有4个字节有效,但是 double *p1可以访问8个字节的内存


下一个测试:

const int a = 10;
//const_cast 中的类型必须是指针、引用或指向对象类型成员的指针
int b = const)cast<int>(a); //编译错误!

const_cast<这里必须是指针或者引用类型>

总结:
1. 我们的 const_cast<>必须和基础类型保持一致
2. const_cast 中的类型必须是指针或者引用

⭐️static_cast

这是我们用的最多的类型转换。

static_cast几乎能做任何类型的转换,不过他主要是提供编译器认为安全的类型转换。

int main () {
	int a = 10;
	char b = static_cast<int>(a);
}

这种类型转换是被允许的。

但是我们看这个例子:

int *p = nullptr;
short* b = static_cast<short*>(p);

编译器报错提示为:static_cast from 'int *' to 'short *' is not allowed
也就是说,当两个类型之间没有任何联系的时候,这种类型转换会被否决

所以该类型转换其实在大部分情况下都给到了我们编译时的检查。


那么基类和派生类类型能不能用static_cast呢?
当然可以了!因为他们就是有联系的。

retinterpret_cast

这就是C++语言级别提供的和C一样的类型转换风格。

⭐️dynamic_cast

支持RTTI类型识别的上下转换,主要用于继承结构中。

举例

我们先定义三个类,其中Base为抽象基类,Derive1、Derive2分别继承自Base类。
并且我们在类A中有虚函数 virtual void func() = 0;
随后我们提供一个统一接口void showFunc(Base *p)来调用func

class Base {
public:
    virtual void func() = 0;
};
class Derive1 : public Base {
public:
    void fun() { cout << "call Derive1::func" << endl; }
};
class Derive2 : public Base {
public:
    void fun() { cout << "call Derive2::func" << endl; }
};

void showFunc(Base *p) {
    p->func(); // 动态绑定
}

我们做如下调用:

int main () {
    Derive1 d1;
    Derive2 d2;
    showFunc(&d1);
    showFunc(&d2);
    return 0;
}

结果如下:

call Derive1::func
call Derive2::func

那么现在我们突然多了一个需求:
我们需要在Derive2里面写一个新需求:

class Derive2 : public Base {
public:
    void func() { cout << "call Derive2::func" << endl; }
    //Derive2实现新功能的API接口函数
    void derive02func() {
    	cout << "call Derive2::derive02func" << endl;
    }
};

也就是说,现在我们showFunc统一接口,当他动态绑定的是Derive2类的话,我希望它能调用新功能derive02func。我们应如何解决呢?

void showFunc(Base *p) {
    p->func(); // 动态绑定
}

dynamic_cast的使用

此时我们需要关注 *p的类型,当它是Derive2类型的,我们就需要去调用derive02func

  • 第一种就是通过typeid(*p).name == "Derive2"来判断是否为Derive2类型
    但其实我们不会使用这种比较字符串的方式来实现。
  • dynamic_cast !
void showFunc(Base *p) {
	//dynamic_cast会检查p指针是否指向的是一个Derive2类型的毒对象
	Derive2 *pd2 = dynamic_cast<Derive2*>(p);
	if (pd2 != nullptr) {
		pd2->derive02func();
	} else {
		p->func();
	}
}

第一部分的语法为,把Base类型的指针转换成Derive2类型的指针。

这是因为首先p指向的对象里有vfptr虚函数指针,它指向了vftable虚函数表,其中放了RTTI信息。
如果是,dynamic_cast转换类型成功,返回Derive2对象的地址给pd2;
否则,dynamic_cast成功失败,返回nullptr;
所以我们有以下语法:

	if (pd2 != nullptr) {
		pd2->derive02func();
	} else {
		p->func();
	}

由于我们的derive02func()是新写的函数,基类中也不存在该虚函数,所以只能是静态绑定,我们也只能通过Derive2类型的指针来进行访问。
现在再进行调用:

int main () {
	Derive1 d1;
	Derive2 d2;
	showFunc(&d1);
	showFunc(&d2);
}

结果如下:

call Derive1::func
call Derive2::derive02func

总结NOTE:
dynamic_cast往往用在继承结构中,特别是存在虚函数表的情况下,该方法对于统一接口的改进,使得多态能够变得更加灵活。

  • 28
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值