【C++】类型转换

📝前言:

这篇文章我们来讲讲C++的类型转换

🎬个人简介:努力学习ing

📋个人专栏:C++学习笔记

🎀CSDN主页 愚润求学

🌄其他专栏:C语言入门基础python入门基础python刷题专栏Linux


文章目录

  • 一,C语言类型转换
    • [1. 隐式类型转换](#1. 隐式类型转换)
    • [2. 显式类型转换](#2. 显式类型转换)
  • 二,C++隐式类型转换(重点)
    • [1. 内置转自定义](#1. 内置转自定义)
    • [2. 自定义转内置](#2. 自定义转内置)
    • [3. 自定义转自定义](#3. 自定义转自定义)
  • 三,C++显式类型转换(重点)
    • [1. 类型安全](#1. 类型安全)
    • [2. 4个显式强制类型转换运算符](#2. 4个显式强制类型转换运算符)
      • [2.1 static_cast](#2.1 static_cast)
      • [2.2 reinterpret_cast](#2.2 reinterpret_cast)
      • [2.3 const_cast](#2.3 const_cast)
      • [2.4 dynamic_cast](#2.4 dynamic_cast)
  • 四,RTTI

一,C语言类型转换

强制类型转换可以发生在转换有意义的地方。什么叫有意义?

  • 比如 intdouble,两个同为表示数据的大小。
  • 比如 指针(地址) 转 int,因为指针地址也是一个数字。但是不能指针转 double

类型转换发生的场景

  • 返回值接受类型和返回类型不相同
  • 形参与实参类型不相同
  • 表达式运算的项类型不相同
  • 赋值运算符,左右两边类型不相同
  • 等等...

1. 隐式类型转换

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

cpp 复制代码
int a = 1;
double b = 2.1;
cout << a + b << endl; // 输出 3.1

在运算时,a被隐式类型转换成了double再进行运算

2. 显式类型转换

显式强制类型转化:需要用户自己去显式在变量前用括号指定要转换的类型

cpp 复制代码
int* ptr1 = &a;
int c = (int)ptr1;
cout << c << endl; // 输出:514849252

二,C++隐式类型转换(重点)

C++支持C语言的类型转换,同时还支持了:自定义类型 → 内置类型、内置类型 → 自定义类型、自定义类型 → 自定义类型

1. 内置转自定义

内置类型转成自定义类型需要构造函数的支持。实际上是:

  1. 把内置类型当参数,调用构造函数创建临时对象
  2. 用临时对象拷贝构造
  3. (不过上面两步可能会被编译器直接优化成一次构造)
cpp 复制代码
class Add
{
public:

	Add(int a)
		:_a(a)
	{ }
	
	Add(int a, int b)
		:_a(a),
		_b(b)
	{ }

	void Print()
	{
		cout << _a + _b << endl;
	}
private:
	int _a = 1;
	int _b = 1;
};


int main()
{
	Add add1 = 2; // 调用第一个拷贝构造
	Add add2 = { 2,2 }; // 调用第二个拷贝构造
	add1.Print(); // 输出 3
	add2.Print(); // 输出 4
	return 0;
}

如果在构造函数之前加explicit 则代表:该构造函数不能被隐式类型转换时调用。
explicit Add(int a)时,Add add1 = 2;会报错,因为创建临时对象时,无法调构造。

2. 自定义转内置

自定义转内置,要求:自定义类型内要支持:operator <转换类型>()的重载

为什么不是 <转换类型> operator(),因为这玩意已经被仿函数占用了

cpp 复制代码
class A
{
public:

	A(double a)
		:_a(a)
	{}

	A(double a, int b)
		:_a(a),
		_b(b)
	{}

	operator double()
	{
		return _a;
	}
private:
	double _a = 1;
	int _b = 1;
};

int main()
{
	A a1 = 2.1;
	double c = a1;
	cout << c << endl; // 输出: 2.1
	return 0;
}

3. 自定义转自定义

自定义A转自定义B:要求B有一个接受A为形参的构造函数(和内置转自定义类似)

cpp 复制代码
class A
{
public:

	A(double a)
		:_a(a)
	{}

	A(double a, int b)
		:_a(a),
		_b(b)
	{}

	double Geta()
	{
		return _a;
	}

private:
	double _a = 1;
	int _b = 1;
};

class B
{
public:
	B(A a1)
		:_a(a1.Geta())
	{
	}

	void Print()
	{
		cout << _a + _b << endl;
	}
private:
	double _a = 1.1;
	int _b = 1;
};

int main()
{
	//A a1 = 2.1;
	//double c = a1;
	//cout << c << endl; // 输出: 2.1

	A a1 = 3.1;
	B b1 = a1;
	b1.Print(); // 输出: 4.1
	return 0;
}

三,C++显式类型转换(重点)

1. 类型安全

类型安全指:编程语言在编译和运行时提供保护机制,避免非法的类型转换和操作。

C++并不是一门类型安全的语言,它允许我们类型转换,尽管我们可能有非法行为(这些非法行为即为不安全)

比如⼀个int*的指针强转成double*,然后用这个指针访问就会出现越界(指针的类型决定的是访问时一次"看"的空间

C++提出了4个显式的命名强制类型转换static_castreinterpret_castconst_castdynamic_cast,为了让类型转换相对而言更安全。(会检查转换是否合理)

2. 4个显式强制类型转换运算符

2.1 static_cast

static_cast

  • 检查时机:编译时
  • 使用场景:
    • 用于基本数据类型(如 int 转 double)、继承关系中的指针/引用转换(向上转换或向下转换,但是要确保安全性)
    • 不能用于无关类型(如 int* 转 double*)或移除 const 属性(把const变量转成非const不行)

示例:

cpp 复制代码
double d = 3.14;
int i = static_cast<int>(d); // 基本类型转换

Base* base = new Derived();
Derived* derived = static_cast<Derived*>(base); // 向下转换(需确保安全)
  • 因为基类指针本身就指向派生类,再转换成派生类,不会有越界问题
  • 但是如果基类指针原本指向的就是基类,直接转换成派生类,就可能"多看"(有访问越界问题)

2.2 reinterpret_cast

reinterpret_cast

  • 检查时机:编译时
  • 用途:用于无关类型之间的转换(完全信任用户自己的行为,但是有极大的安全隐患)
    • int*char*,用户后续的访问行为可能会发生错误,引发未定义行为

示例:

cpp 复制代码
int* ip = new int(42);
char* cp = reinterpret_cast<char*>(ip); // 将 int* 转为 char*

2.3 const_cast

const_cast

  • 检查时机:编译时
  • 用途:添加或移除 constvolatile 属性(但是大多数是移除,因为添加可以隐式类型转换)

示例:

cpp 复制代码
const int x = 10;
int* px = const_cast<int*>(&x); // 移除 const 属性
*px = 20; // 未定义行为(x 可能是常量存储区的值)

volatile T* volatilePtr = ...;
T* nonVolatilePtr = const_cast<T*>(volatilePtr); // 移除 volatile

volatile就是用来确保每次获得变量值都去内存里面重新取,避免优化后直接从寄存器里面取,从而错过变量实际被修改了

2.4 dynamic_cast

dynamic_cast

  • 检查时机:运行时类型检查
  • 用途:用于继承体系中的安全向下转换(将基类指针或引用安全转换成派生类指针或引用)
    • 如果基类指针原本指向的就是派生类,则转换可以成功
    • 如果基类指针原本指向的指向基类,则转换失败(返回nullptr,抛出异常)
  • 要求:基类必须是多态类型(也就是基类中必须有虚函数)
    • (因为dynamic_cast是运行时通过虚表 中(外)存储的type_info判断基类指针指向的是基类对象还是派生类对象)

示例:

cpp 复制代码
class Base { public: virtual ~Base() {} };
class Derived : public Base {};

Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base); // 安全向下转换
if (derived) { /* 成功 */ }

四,RTTI

  • RTTI:运行时类型识别,程序在运行的时候才确定需要用到的对象是什么类型的。
  • RTTI 的代表运算符有两个,typeiddynamic_cast

typeid

  • typeid主要用于返回表达式的类型
  • typeid(e)e可以是任意表达式或类型的名字,typeid(e)的返回值是type_infotype_info派生类对象的引用,type_info可以只支持比较等于和不等于,name成员函数可以返回C风格字符串表示对象类型名字(不同编译器下可能不同)。可以参见:typeinfo头文件
  • e不属于类类型或者是⼀个不包含任何虚函数的类时,typeid返回的是运算
    对象的静态类型(此时是编译时确定)
  • e是定义了至少⼀个虚函数的类的左值时,typeid的返回结果直到运行时才会求得

示例(编译器为vs2022):

cpp 复制代码
#include <typeinfo>

int main() {
    int x = 42;
    // const std::type_info& ti = typeid(x); 
    // typeid 返回的是 const std::type_info&
    std::cout << typeid(x).name() << std::endl;  
    // 输出: int (不同编译器的输出效果可能不同,比如可能拿 i 表示 int)
    return 0;
}

🌈我的分享也就到此结束啦🌈

要是我的分享也能对你的学习起到帮助,那简直是太酷啦!

若有不足,还请大家多多指正,我们一起学习交流!

📢公主,王子:点赞👍→收藏⭐→关注🔍

感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关推荐
虚拟之36 分钟前
36、stringstream
c++
我很好我还能学41 分钟前
【面试篇 9】c++生成可执行文件的四个步骤、悬挂指针、define和const区别、c++定义和声明、将引用作为返回值的好处、类的四个缺省函数
开发语言·c++
蓝婷儿1 小时前
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
开发语言·python·学习
渣渣盟1 小时前
基于Scala实现Flink的三种基本时间窗口操作
开发语言·flink·scala
糯米导航2 小时前
Java毕业设计:办公自动化系统的设计与实现
java·开发语言·课程设计
糯米导航2 小时前
Java毕业设计:WML信息查询与后端信息发布系统开发
java·开发语言·课程设计
南岩亦凛汀2 小时前
在Linux下使用wxWidgets进行跨平台GUI开发
c++·跨平台·gui·开源框架·工程实战教程
MessiGo2 小时前
Javascript 编程基础(5)面向对象 | 5.1、构造函数实例化对象
开发语言·javascript·原型模式
大霞上仙2 小时前
nonlocal 与global关键字
开发语言·python
曦月逸霜2 小时前
第34次CCF-CSP认证真题解析(目标300分做法)
数据结构·c++·算法