当前位置: 首页 > news >正文

C/C++类型转换

目录

C语言中的类型转换

隐式类型转换:

 显示类型转换:

 总结:

C++中的类型转换

static_cast:

reinterpret_cast:

const_cast:

dynamic_cast:

RTTI

C语言中的类型转换

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

隐式类型转换:

编译器在编译阶段自动进行,能转就转,不能转就编译失败。

主要包括:整型家族之间的类型转换整型和浮点型之间bool和整型之间、bool和浮点型之间指针->bool

C++中又引入了内置类型类类型之间(构造函数和操作符重载的支持)和类类型之间的转换

class A
{
public:
	//单参数构造函数,支持隐式类型转换
	A(int a)
		:_a(a)
	{}
private:
	int _a;
};

class B
{
public:
	//全缺省构造函数,支持隐式类型转换
	B(int x = 1,int y = 2)
		:_x(x),_y(y)
	{}
    
    operator int()
	{
		return _x + _y;
	}
private:
	int _x;
	int _y;
};


int main()
{
	char temp = 'a';
	int a = temp;//整型家族之间隐式类型转换

	double b = a;//整型和浮点数之间隐式类型转换

	bool c = a;//bool和整型

	double d = c;//bool和浮点型

	int* p = &a;
	c = p;//指针->bool合法的隐式转换
	//p = c;//bool->指针非法的隐式转换

	A a1 = a;//内置类型->类类型

	B b1 = 20;
    int sum1 = b1;//类类型->内置类型
    int sum2 = (int)b1;

	initializer_list<int> il{ 1,2,3,4,5 };
	vector<int> v{ il };//类类型之间

	return 0;
}

 指针->bool

  • 非空指针​(指向合法内存的指针)会被隐式转换为true
  • 空指针​(NULL)会被隐式转换为false

bool->指针

  • 布尔值(true/false)仅为10,若隐式转为指针,true会被视为非零地址(如0x1),false可能被解释为NULL,但这与bool的原始意图无关,属于未定义行为。
  • 虽然可以通过强制类型转换将bool转为指针(如(int*)true),但这完全依赖编译器和系统实现,属于未定义行为,是极其危险的行为。

C++内置类型->类类型

  • 单参数构造函数或者多参数但只需要传入一个参数的构造函数,支持将内置类型隐式转换为自定义类型,在类与对象一文中有详细讲解。
  • 若要禁止这种隐式转换,需要使用explicit关键字修饰构造函数。

C++类类型->内置类型

  • 需要在类内进行相关的运算符重载(如operator int()),当类对象转换为对应类型时,会自动调用。

C++类类型之间的转换

  • 同样是构造函数支持的。

缺点

由于是编译器自动进行的转换,可能会出现意外截断或提升的问题。

 显示类型转换:

需要用户通过强制类型转换手动指定转换类型。

主要包括:指针类型整型之间、不同类型的指针之间。

int main()
{
	int a = 10;
	int* p1 = (int*)a;//整型->指针
	int b = (int)p1;//整型->指针

	double* p2 = (double*)p1;//int*->double*

	return 0;
}

 缺点

需要用户手动确保安全性,对用户要求较高。

 总结:

  • 尽量避免不必要的类型转换,尤其是隐式转换。

  • 使用显式转换时,确保转换后的值在目标类型的有效范围内。

  • 对指针和函数指针的转换需格外谨慎。

C++中的类型转换

C语言的类型转换格式很简单,但是有不少缺点的:
1. 隐式类型转换有些情况下可能会出问题:比如数据精度丢失
2. 显式类型转换将所有情况混合在一起,代码不够清晰
因此C++提出了自己的类型转化风格,但是因为C++要兼容C语言,所以C++中还可以使用C语言的转换风格的。

C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:static_castreinterpret_castconst_castdynamic_cast

static_cast:

编译时检查,用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用,属于隐式类型转换,但是相较于普通的隐式类型转换,static_cast是可视明确告知了存在类型转换,以及要转换的类型,提高了代码的安全性和可读性

需要注意的是static_cast不能用于不相关的类型之间进行转换

int main()
{
	char temp = 'a';
	double d = 1.23;
	int a = static_cast<int>(d);//double->int
	a = static_cast<int>(temp);//char->int

	//int* p = static_cast<int*>(&d);//错误的,int*和double*是不相关的两个类型

	return 0;
}

reinterpret_cast:

reinterpret_cast是最底层的类型转换运算符,用于执行二进制级别的重新解释。它不进行任何编译时类型安全检查,几乎可以强制转换任何指针或整数类型到其他类型,但使用时需高度谨慎,因为可能导致未定义行为。属于强制类型转换

int main()
{
	double d = 1.23;
	int* p = reinterpret_cast<int*>(&d);//double*->int*
	int b = reinterpret_cast<int>(p);//int*->int

	return 0;
}

const_cast:

const_cast是专门用于添加或移除const和volatile限定符的类型转换运算符。它允许开发者显式绕过类型系统的常量性检查,但需谨慎使用,否则可能导致未定义行为。

int main()
{
	const int a = 10;
	cout << "a:" << a << endl;

	int* p = const_cast<int*>(&a);
	*p = 3;
	cout << "*p:" << *p << endl;
	cout << "a:" << a << endl;

	return 0;
}

上边的代码通过const_cast<int*>将const int*类型的&a转换为int*类型,并赋值给p,再通过指针p修该a的值,编译运行:

可以发现a的值并没有改变,是因为const_cast操作符没有作用吗?但是否定的,a的值之所以没有改变是因为编译器会把const常量的值存放在寄存器中每次读取时直接去寄存器中读取数据,而通过指针p修改的是a在内存中的数据,但是并不会影响寄存器中的数据,因此再次输出a的值时会显示没有改变。要想保持寄存器和内存中数据的一致性,可以通过volatile修饰变量实现。

int main()
{
	//const int a = 10;
	volatile const int a = 10;
	cout << "a:" << a << endl;

	int* p = const_cast<int*>(&a);
	*p = 3;
	cout << "*p:" << *p << endl;
	cout << "a:" << a << endl;

	return 0;
}

dynamic_cast:

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(dynamic_cast会进行类型检查,保证安全性,常规的强转或者使用reinterpret_cast/static_cast也可以实现,但是它们不会进行类型检查,存在风险)

注意:
1. dynamic_cast只能用于父类含有虚函数的类。
2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0。

class A
{
public:
	virtual void f() {}
};
class B : public A
{};
void fun(A* pa)
{
	// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
	B* pb1 = static_cast<B*>(pa);
	B* pb2 = reinterpret_cast<B*>(pa);
	B* pb3 = dynamic_cast<B*>(pa);
	cout << "pb1:" << pb1 << endl;
	cout << "pb2:" << pb2 << endl;
	cout << "pb3:" << pb3 << endl;
}
int main()
{
	A a;
	B b;
	fun(&a);
	fun(&b);
	return 0;
}

上述代码定义了一个含有虚函数的基类A,通过A派生出B类,通过fun函数,分别传入A*参数和B*参数,在fun函数中对参数进行类型转换,观察结果:

可以发现dynamic_cast的类型转换是更加安全的,当想要将一个实际指向父类的指针转换为子类指针时,dynamic_cast无法完成转换,保证了安全性。

RTTI

RTTI(Runtime Type Information)即运行时类型识别,是C++中用于在程序运行时获取对象类型信息的机制,主要支持dynamic_cast和typeid操作。

typeid:返回一个type_info对象的指针,描述表达式在运行时的类型。

dynamic_cast:在多态场景下,将基类指针/引用安全地转换为派生类类型,需运行时检查类型是否合法。

除了运行时获取类型信息,还有编译时获取类型信息:

decltype:是在编译时获取类型信息。

int main()
{
	int a = 10;
	decltype(a) b;
	cout << typeid(b).name() << endl;

	return 0;
}

上述代码中decltype获取a的类型后,又定义了b,通过typeid输出b的类型。

相关文章:

  • 扎实基础:测试工作的基石
  • Flask 框架简介
  • Manus+Ollama实现本地大模型部署和应用测试
  • 基于multisim的花样彩灯循环控制电路设计与仿真
  • 密码学(终极版)
  • Django系列教程(5)——Django模型详解
  • 物联网设备接入系统后如何查看硬件实时数据?
  • 【摸鱼指南】--- VSCode 使用 Thief-Book 隐形阅读模式配置教程 程序员必备插件
  • 子集(78)
  • Docker 深入解析 Docker 配置文件:/etc/docker/daemon.json
  • 操作系统——进程与线程
  • 用CMake编译glfw进行OpenGL配置,在Visual Studio上运行
  • python爬虫系列课程8:js浏览器window对象属性
  • K8s构建带有maven环境的jenkins镜像
  • Fragment 懒加载的优化方案
  • vuejs 模板语法、条件渲染、v-for、事件处理、表单输入绑定
  • 01.04、回文排序
  • Linux基础---切换用户、创建用户、删除用户、添加和删除用户组、修改密码
  • OSPF的各种LSA类型,多区域及特殊区域
  • day1 postman重置密码,提交(submit)没有任何反应或者会提示超时
  • 重庆网站建设仿站/培训班招生方案有哪些
  • 官方网站建设必要性/游戏推广对接平台
  • 做编程网站/网络营销网络推广
  • 遵义网站开发公司/江苏seo排名
  • 做网站哪里的好/怎么做网站优化排名
  • 深圳最大的手机市场在什么地方/一个网站的seo优化有哪些