文章目录
- 类型转换
- 1.C语言的类型转换
- 1.1整形提升
- 1.2算术转换
- 1.3强制类型转换
- 2.C++类型转换
- 2.1static_cast
- 2.2reinterpret_cast
- 2.3const_cast
- 2.3dynamic_cast
- 总结
类型转换
1.C语言的类型转换
1.1整形提升
在写顺序表的插入函数时,我们的接口实现是这样的:
void intsert(Seq* s,size_t pos,int val)
{
int end = s->size - 1;
while(end >= pos)
{
s->arr[end + 1] = s->arr[end];
end--;
}
//此时我们已经写出了一个bug
}
当插入的位置为0时,由于pos位置为size_t类型的,此时会导致end在比较时会发生整形提升,自动的转换为unsigned int 类型进行比较,由于unsigned int 类型大于等于0,因此到0之后再减减进行比较是不会停止的。
- 截断
int main()
{
int a = 257;
char b = a;
return 0;
}
- 由于存储数据的范围不一样或者说是内存不一样,所以在拷贝内存的数据时,会发生只拷贝部分内存的现象,这种现象我们称之为截断。
1.2算术转换
#include<stdio.h>
int main()
{
int a = 2;
double b = a;//也叫隐式类型转换
float c = (float) a;//也叫显示类型转换
printf("%lf", b);
return 0;
}
从内存的角度进行理解,由于int和double的存储方式不一样,因此需要按照某种规则进行转换,而不是直接的将内存进行拷贝。
1.3强制类型转换
int main()
{
int a = 2;
double b = a;
int* ptr = (int*)a;
int* ptr1 = (int*)&b;
//注:int* ptr2 = (int*)b;——类型转换无效,跨度太大了。
return 0;
}
- 指针和整形是两种用途完全不同的类型,这两种几乎用途不想关的类型,可以通过强转,而关联起来,除此之外不同类型的指针指向的类型不同,也可以通过强转而改变类型,本质上是改变计算机从内存解释数据的角度。
C语言的类型转换,从总体上看是比较容易出错的,也不太规范
,因为从我们举的第一个例子看是很容易出错的,而且即使所有的C类型的显示转换的形式是一样的,但还是有本质区别的。
2.C++类型转换
2.1static_cast
- 用于编译器支持的隐式类型转换。
- 非多态类型的转换(向下转换不保证安全性)。
- 空指针的转换以及任意指针转void*
- 注意:转换时,不能丢掉const或者其他类型限定符。
class A
{
};
class B : public A
{
public:
void func()
{
cout << "B:func()" << endl;
}
private:
int _a = 0;
};
//class 默认私有继承,struct默认公有继承。
int main()
{
int n1 = 0;
float f1 = 0.0f;
double d1 = 0.0;
char c = 'a';
//1.用于编译器支持的隐式类型转换。
n1 = static_cast<int>(f1);
n1 = static_cast<int>(d1);
n1 = static_cast<int>(c);
//2.非多态类型的转化。
A a;
B b;
A& ra = static_cast<A&>(b);//向上转换是安全的。
B& rb = static_cast<B&>(a);//向下转换是不安全的。
//3.空指针转换为任何类型的指针
A* a_ptr = static_cast<A*>(nullptr);
int* i_ptr = static_cast<int*>(nullptr);
//4.所有指针转换为void*类型的
void* vptr1 = static_cast<void*>(a_ptr);
vptr1 = static_cast<void*>(i_ptr);
//5.无法丢掉常量或其他类型限定符
volatile const int n3 = 0;//说明:volatile每次从内存中取数据。
volatile const int& rn3 = static_cast<volatile const int&>(n3);
return 0;
}
2.2reinterpret_cast
- 只适用于不安全的类型之间的转换。
class B
{};
class A
{};
int main()
{
int n1 = 0;
float f1 = 0.0f;
double d1 = 0.0;
char c = 'a';
//1.只能用于适合强转(不安全的转换)的类型,不能用于相关编译器支持的隐式转换(安全)的类型转换。
//如:不同类型的指针转换或者不同类型的引用,或者其他类型转指针的转换
int& r = reinterpret_cast<int&>(f1);
int* p = reinterpret_cast<int*>(&f1);
B b;
A a;
A* a_ptr = &a;
B* b_ptr = reinterpret_cast<B*>(a_ptr);
//2.不能用于类之间的转换
//B b = reinterpret_cast<B>(A());
//3.无法丢掉常量或者其他类型限定符
const int i_a = 0;
//int& ra = reinterpret_cast<int&>(i_a);
return 0;
}
2.3const_cast
- 最为关键的作用就是去掉const属性,只能调节类型限定符,不能修改类型。
int main()
{
//去掉const属性
const int a = 0;
int& ra = const_cast<int&>(a);
ra = 1;
//指针可以去掉两个const
const int* const p = &a;
int*& p1 = const_cast<int*&>(p);
return 0;
}
2.3dynamic_cast
- 主要功能实现多态类型的向下转换
- 指针类型如果转化失败,则为空指针。
- 引用如果转换失败,则直接抛异常——Bad dynamic_cast!
细节:必须为多态类型,因为是否能转化成功要看虚表
class A
{
virtual void func()
{}
};
class B : public A
{};
void func(A* ptr)
{
B* ptr_b = dynamic_cast<B*>(ptr);//必须是多态类型的,即有虚表。
if (ptr_b == nullptr)
{
cout << "转换失败" << endl;
}
else
{
cout << "转换成功" << endl;
}
}
void func(A& ra)
{
B& rb = dynamic_cast<B&>(ra);
cout << "转换成功" << endl;
}
int main()
{
A* ptr_a = new A;
B* ptr_b = new B;
func(ptr_a);
func(ptr_b);
try
{
func(*ptr_b);
func(*ptr_a);
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
-
那dynamic是如何进行实现的呢?
-
基本原理:RTTI,Run-time Type identification的简称,即:运行时类型识别。
-
实现方式: 1. typeid运算符 2. dynamic_cast运算符 3. decltype
模型图:
每个类都保存有自己的类型信息(具体是保存在虚表中),并通过
继承方式连接起来
,再检查是否为安全的类型转换时,通过向上查找
,看是否存在要转化的对象,如果有便转换成对象,如果没有便抛异常或者进行报错。
- 查找规则:当使用 dynamic_cast 对指针进行类型转换时,会先找到该指针指向的对象,再根据对象找到当前类(指针指向的对象所属的类)的类型信息,并从此节点开始沿着继承链向上遍历(注意是向上),如果找到了要转化的目标类型,那么说明这种转换是安全的,就能够转换成功,如果没有找到要转换的目标类型,那么说明这种转换存在较大的风险,就不能转换。
- 作用对象:注意dynamic_cast转换符只能用于含有虚函数的类。
总结
今天的分享就到此结束了,我是舜华,期待与你的下一次相遇!