【C++进阶之路】类型转换

文章目录

  • 类型转换
    • 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
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(A());
	
	//3.无法丢掉常量或者其他类型限定符 
	const int i_a = 0;
	//int& ra = reinterpret_cast(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

  • 主要功能实现多态类型的向下转换
  1. 指针类型如果转化失败,则为空指针。
  2. 引用如果转换失败,则直接抛异常——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

模型图:
【C++进阶之路】类型转换_第1张图片

每个类都保存有自己的类型信息(具体是保存在虚表中),并通过继承方式连接起来,再检查是否为安全的类型转换时,通过向上查找,看是否存在要转化的对象,如果有便转换成对象,如果没有便抛异常或者进行报错。

  • 查找规则:当使用 dynamic_cast 对指针进行类型转换时,会先找到该指针指向的对象,再根据对象找到当前类(指针指向的对象所属的类)的类型信息,并从此节点开始沿着继承链向上遍历(注意是向上),如果找到了要转化的目标类型,那么说明这种转换是安全的,就能够转换成功,如果没有找到要转换的目标类型,那么说明这种转换存在较大的风险,就不能转换。
  • 作用对象:注意dynamic_cast转换符只能用于含有虚函数的类。

总结

 今天的分享就到此结束了,我是舜华,期待与你的下一次相遇!

你可能感兴趣的:(C++进阶之路,c++,开发语言)