• 【C++练级之路】【Lv.26】类型转换




    快乐的流畅:个人主页


    个人专栏:《算法神殿》《数据结构世界》《进击的C++》

    远方有一堆篝火,在为久候之人燃烧!

    一、C风格类型转换

    1.1 隐式类型转换

    隐式类型转换(Implicit Type Conversion),又称自动类型转换,由编译器在编译阶段自动进行,能转就转,不能转就编译失败。

    常见的隐式类型转换包括:

    • 赋值运算符中的类型转换:例如从 int 赋值转换为 double
    • 算术运算符中的类型转换: 例如 intlong 进行运算,会被提升为 long 。同时,超出类型的表示范围,会进行符号扩展或截断。
    • 函数调用中的类型转换:实参与形参类型不匹配,会尝试转换为与形参匹配的类型。
    • 布尔值中的类型转换:对于数值类型,非零值转换为 true ,零值转换为 false ;对于指针类型,非空指针转换为 true ,空指针转换为 false
    • 指针中的类型转换:指针可以隐式转换为 void* 类型。数组名会被转换为指向数组第一个元素的指针,函数名会被转换为指向该函数的指针。
    void func(double d){}
    
    void test()
    {
    	int i = 1;
    	double d = i;//赋值运算符
    	double ret = i + d;//算术运算符
    	func(i);//函数调用
    	void* p = &i;//指针
    }
    

    1.2 显式类型转换

    显式类型转换(Explicit Type Conversion),又称为强制类型转换,需要程序员显式地指定转换方式。

    格式:(type)expression

    void test()
    {
    	int i = 1;
    	double d = (double)i;
    }
    

    二、C++风格类型转换

    2.1 static_cast

    格式:static_cast(expression)

    用于非多态类型的转换(静态转换),并且只适用相近类型之间的转换,对应C的隐式类型转换,例如基本数据类型之间的转换。

    void test()
    {
    	int i = 1;
    	double d = static_cast<double>(i);//静态转换,相近类型
    	int* pi = &i;
    	void* p = static_cast<void*>(pi);
    }
    

    2.2 dynamic_cast

    格式:dynamic_cast(expression)

    用于多态类型的转换(动态转换),并且用于向下转型:将一个父类对象的指针/引用转换为子类对象的指针或引用。

    ps:向上转型,没有类型转换,属于赋值兼容规则。

    class A
    {
    public:
    	virtual void f(){}
    
    	int _a = 1;
    };
    
    class B :public A
    {
    public:
    	virtual void f(){}
    
    	int _b = 2;
    };
    
    void test()
    {
    	B b;
    	A a = b;
    	A& ra = b;//此处没有隐式类型转换
    
    	double d = 3.14;
    	const int& ri = d;//此处有隐式类型转换
    }
    

    由于类型转换后的临时对象具有常性,所以要用常引用。而向上转型(切片)不用常引用,所以没有发生类型转换。


    dynamic_cast 在运行时检查转换是否有效。如果转换无效,则指针类型返回空指针 nullptr,引用类型抛出异常 std::bad_cast

    void func(A* pa)
    {
    	//B* p = (B*)pa;//直接向下转换,如果指向父类,则存在越界风险
    	B* p = dynamic_cast<B*>(pa);
    	if (p)
    	{
    		p->_a++;
    		p->_b++;
    	}
    	else
    	{
    		cout << "转换失败" << endl;
    	}
    }
    
    void test()
    {
    	B b;
    	A a = b;
    	func(&a);
    	func(&b);
    }
    

    直接向下转换,如果指向父类,则存在越界风险。运用 dynamic_cast 则可安全地进行向下转型。

    2.3 const_cast

    格式:const_cast(expression)

    用于修改对象的常量属性,可添加或移除变量的 constvolatile 属性。

    void test()
    {
    	const int a = 2;
    	int* p = const_cast<int*>(&a);
    	*p = 3;
    	
    	cout << a << endl;
    	cout << *p << endl;
    
    	cout << &a << endl;
    	cout << p << endl;
    }
    

    通过打印可以发现,a的值依旧为2,而*p的值为3,并且它们的地址相同。这是为什么呢?其实,编译器在这里做了优化,常变量a会被放进寄存器或者直接替换

    这时我们可以加上 volatile 关键字,其作用是阻止编译器对标记的变量进行某些优化。

    void test()
    {
    	volatile const int a = 2;
    	int* p = const_cast<int*>(&a);
    	*p = 3;
    	
    	cout << a << endl;
    	cout << *p << endl;
    
    	//cout << &a << endl;
    	printf("%p\n", &a);
    	cout << p << endl;
    }
    

    此时还有一个问题,那就是用 volatile 修饰的变量类型无法和参数为 void*operator<< 函数匹配,从而会匹配到不合适的重载函数,输出错误的地址值。所以,此处我们用 printf 进行打印即可。

    2.4 reinterpret_cast

    格式:reinterpret_cast(expression)

    为操作数的位模式提供较低层次的重新解释。用于不相近类型之间的转换,对应C的显式类型转换。

    void test()
    {
    	int i = 1;
    	int* p = &i;
    	//double* p1 = static_cast(i);//error
    	double* p2 = reinterpret_cast<double*>(i);
    	//double* p3 = static_cast(p);//error
    	double* p4 = reinterpret_cast<double*>(p);
    }
    

    三、RTTI

    RTTI(Run-Time Type Identification)是C++语言中的一个特性,它允许程序在运行时确定对象的实际类型

    3.1 typeid

    用于打印变量的类型。

    void test()
    {
    	auto f = [](int x, int y){return x + y;};
    	cout << typeid(f).name() << endl;
    }
    

    3.2 dynamic_cast

    class Base
    {};
    class Derive :public Base
    {};
    
    void func(Base* pb)
    {
    	Derive* pd = dynamic_cast<Derive*>(pb);
    }
    

    3.3 decltype

    可将变量的类型提取,作为参数传递。

    void test()
    {
    	int x = 1;
    	double y = 2.0;
    
    	auto ret = x * y;
    	vector<decltype(ret)> v;
    }
    

    总结

    C风格的类型转换,将不同类型的转换混在一起,不够清晰,可视性差,难以跟踪错误的转换。所以我们推荐使用C++的类型转换,加强类型转换的可视性,提高代码的可读性与可维护性。


    一般来说:

    • 使用 static_cast 进行相近类型的转换。
    • 使用 dynamic_cast 在运行时进行多态类型检查。
    • 使用 const_cast 移除或添加常量性。
    • 使用 reinterpret_cast 进行不相近类型的转换。

    真诚点赞,手有余香

  • 相关阅读:
    uni-app的下拉搜索选择组合框
    串的KMP算法匹配实现
    geotools实现坐标系转换
    【奇思妙想】【节省磁盘空间】我有一些文件,我不想移动它们,但又想节省磁盘空间,该怎么做呢?
    神经网络前向传播表达式,神经网络的前向传播
    JSONObject和JSONArray区别及注意事项
    TiUP 镜像参考指南
    transformers - 预测中间词
    OpenCV项目开发实战--实现面部情绪识别对情绪进行识别和分类及详细讲解及完整代码实现
    vue基础-动态class、动态style、vue过滤器、vue计算属性vue基础-动态class、动态style、vue过滤器、vue计算属性
  • 原文地址:https://blog.csdn.net/2301_79188764/article/details/139935155