【C++】类型转换

2023-10-17 08:45:06 浏览数 (1)

一、C语言中的类型转换

在 C 语言中,如果 赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与 接收返回值类型不一致时,就需要发生类型转化 , C 语言中总共有两种形式的类型转换: 隐式类型 转换和显式类型转换

1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败

2. 显式类型转化:需要用户自己处理

代码语言:javascript复制
int main()
{
	//隐式类型转换
	int i = 5;
	double d = i;
	//显式类型转换
	int* pi = &i;
	int address = (int)pi;

	return 0;
}

C风格的转换格式很简单,但是有不少缺点的:

1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失 2. 显式类型转换将所有情况混合在一起,代码不够清晰

因此 C 提出了自己的类型转化风格,注意 因为 C 要兼容 C 语言,所以 C 中还可以使用 C 语言的 转化风格


二、C 中的类型转换

标准 C 为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符: static_cast、reinterpret_cast、const_cast、dynamic_cast

1.static_cast

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。

例如:

我们可以用于将 double 类型转化成int类型,但是不可以将 double *类型转化成int类型。

代码语言:javascript复制
int main()
{
	double d = 5.32;
	int i = static_cast<int>(d);

	double* pd = &d;
	int di = static_cast<int>(pd);
	cout << di << endl;
	return 0;
}

2.reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型。

例如:

我们上面无法将double*转化成int类型,用reinterpret_cast就可以实现。

代码语言:javascript复制
int main()
{
	double d = 5.32;
	double* pd = &d;
	int di = reinterpret_cast<int>(pd);
	cout << di << endl;
	return 0;
}

3.const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值

例如:

将const类型的常变量转化为没有const属性的类型

代码语言:javascript复制
int main()
{
	const int a = 2;
	int* p = const_cast<int*> (&a);
	*p = 3;

	cout << a << endl;
	cout << *p << endl;
	return 0;
}

看到结果,很多人可能就会好奇了,怎么还会打印不同的结果呢?

这是因为,我们用const修饰a类型之后,编译器认为a之后都不会改变,就将a读取到寄存器中,之后读取a的数据都是用存储在寄存器中的数据。当我们用*p修改了a在内存中的值之后,打印a是打印寄存器中a的值,打印*p则是内存中a的值。所以结果会不同。

拓展:volatile

volatile是一个关键字,作用是保持内存的可见性,也就是每次读取数据都会从内存中读取。

代码语言:javascript复制
int main()
{
	volatile const int a = 2;
	int* p = const_cast<int*> (&a);
	*p = 3;

	cout << a << endl;
	cout << *p << endl;
	return 0;
}

4.dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)

向上转型:子类对象指针 / 引用 -> 父类指针 / 引用 ( 不需要转换,赋值兼容规则 )

向下转型:父类对象指针 / 引用 -> 子类指针 / 引用 ( dynamic_cast 转型是安全的 )

注意:

1. dynamic_cast 只能用于父类含有虚函数的类(如果是子类含有虚函数而父类不具有,那么无法转换)

2. dynamic_cast 会先检查是否能转换成功,能成功则转换,不能则返回 0

 代码示例:

当我们用强制类型转换的时候,是可能会发生越界问题的。

代码语言:javascript复制
class Father
{
public:
	virtual void f(){}
private:
	int _a;
};

class Child :public Father
{
public:
	int _b = 2 ;
};

void Func(Father* f)
{
	Child* ch = (Child*)f;
	cout << ch->_b << endl;
}

int main()
{
	Father* th = new Father;
	Child* ch = new Child;
	Func(th);
	Func(ch);
	return 0;
}

 我们再用dynamic_cast来将父类转换成子类

代码语言:javascript复制
class Father
{
public:
	virtual void f(){}
private:
	int _a;
};

class Child :public Father
{
public:
	int _b = 2 ;
};

void Func(Father* f)
{
	Child* ch = dynamic_cast<Child*>(f);
	if (ch == 0)
	{
		cout << "转换失败" << endl;
		return;
	}
	cout << ch->_b << endl;

}

int main()
{
	Father* th = new Father;
	Child* ch = new Child;
	Func(th);
	Func(ch);
	return 0;
}

 我们可以看到,会出现越界问题的就会转化失败返回0。

这样使得我们的程序更加安全。


三、RTTI

RTTI : Run-time Type identifification 的简称,即:运行时类型识别。

C 通过以下方式来支持 RTTI :

1. typeid运算符

2. dynamic_cast运算符

3. decltype

1.typeid运算符

作用:一般我们使用typeid来查看变量的类型。 

格式:typeid(变量).name()

代码示例: 

代码语言:javascript复制
int main()
{
	int a = 0;
	cout << typeid(a).name() << endl;
	return 0;
}

2. decltype

作用:复制变量的类型作为使用。

格式:decltype(变量) 新变量名

代码语言:javascript复制
int main()
{
	int a = 0;
	decltype(a) b = 10;
	cout << b << endl;
	return 0;
}

我们有时候可能会忘记了某个变量的名字,但是查找定义又十分麻烦,为了便于创建一个同类型的变量,我们可以使用decltype。

0 人点赞