• C++ 命名类型转换


    传统艺能😎

    小编是双非本科大二菜鸟不赘述,欢迎米娜桑来指点江山哦
    在这里插入图片描述
    1319365055

    🎉🎉非科班转码社区诚邀您入驻🎉🎉
    小伙伴们,满怀希望,所向披靡,打码一路向北
    一个人的单打独斗不如一群人的砥砺前行
    这是和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
    社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
    直达: 社区链接点我


    在这里插入图片描述

    类型转换🤔

    任何一门语言里面都会遇到赋值符号两边不同的情况,或者形参和实参不匹配,这种情况就存在类型转换,特别是 C++ 这种强类型语言,即使不发生显示的强制转换也会进行隐式类型转换

    其实在C语言阶段就早有提出两种类型转换方式:

    1. 显示类型转换:需要手动转换成需要的类型对象:(指定类型)变量
    2. 隐式类型转换:在代码编译阶段自动执行,能转就转,不能转就编译失败

    当然并不是显示转换就可以随心所欲的转换, 只有相近类型之间才能发生隐式类型转换 \color{red} {只有相近类型之间才能发生隐式类型转换} 只有相近类型之间才能发生隐式类型转换,比如 int 和 double 表示的都是数值,不过它们的范围和精度不同。而指针类型表示的是地址编号,因此整型和指针类型之间不会进行隐式类型转换,如果需要转换则只能进行显式类型转换:

    int main()
    {
    
    	//显式类型转换
    	int* p = &i;
    	int address = (int)p;
    	cout << p << endl;
    	cout << address << endl;
    	return 0;
    	
    	//隐式类型转换
    	int i = 1;
    	double d = i;
    	cout << i << endl;
    	cout << d << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    四大类型转换🤔

    介于类型转换的场合非常多,且存在一些复杂场景的类型转换,单纯的 C 语言的类型转换虽然简单,但是存在精度丢失、可视性差等缺陷,于是 C++ 标准推出了四大命名类型转换操作符:

    static_cast
    reinterpret_cast
    const_cast
    dynamic_cast

    static_cast😋

    static_cast 用于相近类型之间的转换,编译器隐式执行的任何类型转换都可以用 static_cast,但它不能用于不相关类型之间的转换:

    int main()
    {
    	double d = 12.34;
    	int a = static_cast<int>(d);
    	cout << a << endl;
    
    	int* p = &a;
    	// int address = static_cast(p); //error
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    reinterpret_cast😋

    reinterpret_cast 用于两个不相关类型之间的转换:

    int main()
    {
    	int a = 10;
    	int* p = &a;
    	int address = reinterpret_cast<int>(p);
    	cout << address << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    他除了用于两个不相关类型的转换,还有一个强有力的功能:可以将带参带返回值的函数指针转换成了无参无返回值的函数指针,并且此时转换后函数指针还可以调用这个函数:

    typedef void(*FUNC)();
    int DoSomething(int i)
    {
    	cout << "DoSomething: " << i << endl;
    	return 0;
    }
    int main()
    {
    	FUNC f = reinterpret_cast<FUNC>(DoSomething);
    	f();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    用转换后的函数指针调用该函数时没有传入参数,因此这里打印出参数 i 的值是一个随机值

    const_cast😋

    const_cast 用于删除变量的 const 属性,转换后就可以对 const 变量的值进行修改:

    int main()
    {
    	const int a = 2;
    	int* p = const_cast<int*>(&a);
    	*p = 3;
    	cout << a << endl;  //2
    	cout << *p << endl; //3
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    代码中用 const_cast 删除了变量 a 地址的 const 属性,这时就可以通过这个指针来修改变量 a 的值

    由于编译器认为 const 修饰的变量是不会被修改的,因此会将 const 修饰的变量存放到寄存器当中,当需要读取 const 变量时就会直接从寄存器中进行读取, 而我们修改的实际上是内存中的 a 的值,因此最终打印出 a 的值是未修改之前的值 \color{red} {而我们修改的实际上是内存中的 a 的值,因此最终打印出a的值是未修改之前的值} 而我们修改的实际上是内存中的a的值,因此最终打印出a的值是未修改之前的值

    如果不想让 const 变量被优化到寄存器当中,可以用 volatile 关键字对 const 变量进行修饰,这时当要读取这个 const 变量时编译器就会从内存中进行读取,即保持了该变量在内存中的可见性

    dynamic_cast😋

    dynamic_cast 用于将父类的指针(或引用)转换成子类的指针(或引用),这就不得不提一下向上转型和向下转型了

    其实早在继承和多态的博客中说过,向上转型是子类的指针(或引用)→ 父类的指针(或引用);向下转型是 父类的指针(或引用)→ 子类的指针(或引用)。向上转型就是所说的切割/切片,是语法天然支持的,不需要进行转换,而向下转型是语法不支持的,需要进行强制类型转换

    1. 其中向下转型是存在安全问题的:如果父类指向的是一个父类对象,那么将其转换为子类是不安全的,因为转换后可能会访问到子类的资源,而这个资源是父类对象所没有的
    2. 如果父类的指针(或引用)指向的是一个子类对象,那么将其转换为子类的指针(或引用)则是安全的

    若果要用 C 语言的强制类型转换进行向下转型也是不安全的,因为无论父指向的是父类对象还是子类对象都会进行转换。而使用 dynamic_cast 进行向下转型则是安全的,但如果父类指向的是父类对象那么 dynamic_cast 会转换失败并返回空指针!

    class A
    {
    public:
    	virtual void f()
    	{}
    };
    class B : public A
    {};
    void func(A* pa)
    {
    	B* pb1 = (B*)pa;               //不安全
    	B* pb2 = dynamic_cast<B*>(pa); //安全
    
    	cout << "pb1: " << pb1 << endl;
    	cout << "pb2: " << pb2 << endl;
    }
    int main()
    {
    	A a;
    	B b;
    	func(&a);
    	func(&b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    dynamic_cast 只能用于含有虚函数的类,因为运行时类型检查需要运行时的类型信息,而这个信息存储在虚函数表中,只有定义了虚函数的类才有虚函数表!

    explicit🤔

    explicit 用来修饰构造函数,从而禁止单参数构造函数的隐式转换:

    class A
    {
    public:
    	explicit A(int a)
    	{
    		cout << "A(int a)" << endl;
    	}
    	A(const A& a)
    	{
    		cout << "A(const A& a)" << endl;
    	}
    private:
    	int _a;
    };
    int main()
    {
    	A a1(1);
    	//A a2 = 1; //error
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在语法上,代码中的A a2 = 1等价于以下两句代码:

    A tmp(1);  //先构造
    A a2(tmp); //再拷贝构造
    
    • 1
    • 2

    可见和上述这种用临时变量拷贝构造的方法不同,现在的编译器做了优化,当遇到 A a2 = 1这句代码时,会直接按照 A a2(1) 的方式进行处理,这也叫做隐式类型转换

    对于单参数的自定义类型来说,A a2 = 1 这种代码可读性不是很好,因此可以用 explicit 修饰单参数构造函数,从而禁止单参数构造函数的隐式转换

    RTTI🤔

    RTTI 就是运行时类型识别,C++ 通过以下几种方式来支持 RTTI:

    1. typeid:在运行时识别出一个对象的类型。
    2. dynamic_cast:在运行时识别出一个父类的指针(或引用)指向的是父类对象还是子类对象。
    3. decltype:在运行时推演出一个表达式或函数返回值的类型
  • 相关阅读:
    JavaScript项目1_猜数字(前导)
    04.Finetune vs. Prompt
    web前端面试高频考点——Vue3.x新增API(生命周期,ref、toRef 和 toRefs 的理解和最佳使用方式)
    PC应用管理工具 连接流程图 支持所有android手机或设备 批量设备批量应用安装卸载等管理 OS升级 push文件夹等
    Flink—— Data Source 介绍
    【文献阅读】移动边缘计算中基于CNN模型分割的计算适配和负载均衡研究_黄煜
    可复现的语言大模型推理性能指标
    Docker容器获取宿主机信息
    HTML 简介
    园区网典型配置案例
  • 原文地址:https://blog.csdn.net/qq_61500888/article/details/127551855