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

[C++] C、C++类型转换

标题:[C++] C、C++类型转换

@水墨不写bug



目录

一、C的类型转换 

 二、C++的类型转换

1.static_cast(对应隐式类型转换)

2.reinterpret_cast(对应强制类型转换)

3.const_cast(对应强制类型转换中有风险的去掉const属性)

4.dynamic_cast


正文开始:

一、C的类型转换 

        C++的类型转换是对C语言类型转换的规范。

        在C语言中,发生的类型转换有两种:隐式类型转换和显示类型转换;如果赋值符号左右两侧的类型不同,或者形参与实参类型不匹配,或者返回值类型与接受返回值类型不一致时,就会发生类型转换。


隐式类型转换

        隐式类型转换编译器在编译阶段自动转换,如果可以转换,则没有提醒;如果不能转换,则编译失败。

显示类型转换

        需要我们自己手动处理,如用到“()”的强制类型转换。


         C语言的隐式类型转换我们在C语言阶段学习过,它遵循一定的规则,这里我们举一些例子:

        bool与整形的相互转换:非0的任意整形为真true,0为假false;true转为整形为1,false转为整形为0;

        浮点型与整形的相互转换:浮点数赋值给整形,只做近似处理。结果仅仅保留浮点数小数点之前的整数部分;整数赋值给浮点数,小数部分记为0,整数部分保留。

        无符号类型的超范围赋值:如果给一个无符号类型赋一个超出范围的值,结果是对无符号类型表示数值总数取模后的余数。

        有符号类型的超范围赋值:给有符号类型超范围赋值,结果未定义!


除此之外,内置类型与自定义类型之间相互转换 :内置类型可以通过构造函数隐式类型转换为自定义类型:单参数构造函数支持直接赋值,多参数构造函数支持用“{}”花括号赋值。

#include<iostream>
using namespace std;
class A{public:A(int a = -1):_a(a),_b(a){}int _a;int _b;
};
class AA{public:AA(int a1 = -1,int a2 = -1):_a(a1), _b(a2){}int _a;int _b;
};
int main()
{A aa1 = 1;//单参数构造函数支持直接用内置类型隐式类型转换AA aa2 = { 1,2 };//多参数构造函数可以使用花括号实现隐式类型转换return 0;
}

         自定义类型也可以隐式类型转换为内置类型,需要通过特定的成员函数实现:

这个成员函数是一个有着特殊实现语法的函数,实现的语法格式如下:

operator /*需要转换成的类型*/()
{//具体的实现没有限制,可以对这个对象内部的数据进行运算,最终返回结果即可return /*返回需要转换成的类型即可*/
}

 比如,实现将下面这个类型隐式类型转换为double:


class A
{
public:int _a;double _b;
};

 那么就需要实现这样的一个函数:(函数内部如何实现没有限制,只要实现了A到double类型的转换即可)

    operator double(){return _a + _b;}

于是,可以进行A到double的隐式类型转换了:
         


class A
{
public:A(int a = -1,double b = -1):_a(a),_b(b){}operator double(){return _a + _b;}int _a;double _b;
};int main()
{A aa1(4, 8.7);double f = aa1;//f 的值为 12.7return 0;
}

         自定义类型和自定义类型也是可以实现相互转换的,本质就是借助对应的构造函数:

class A{
public:A(int a = -1,int b = -1):_a(a),_b(b){}const int get() const{return _a + _b;}int get(){return _a + _b;}
private:int _a;int _b;
};
class B{
public:B(int c = -1):_c(c){}B(const A& aa):_c(aa.get()){}int _c;
};
int main()
{A aa = { 2,5 };B bb;bb = aa;//B提供了从A构造B的构造函数,所以可以实现A隐式类型转换到Bcout << bb._c << endl;//  7return 0;
}

        强制类型转换也只是能将有一定关系的类型相互强制转换。相反,如果两个类型相差十分大,强制类型转换也无能为力。

缺陷:

        转换的可视性比较差,所有的转换形式都是以一种相同的形式书写,难以跟踪错误的转换。

 二、C++的类型转换

         注意C++是兼容C的,所以在C++程序中,也会出现C的类型转换。由于C的类型转换混合杂糅,难以区分,十分模糊,所以C++提出了自己的一套类型转换逻辑。

        这些类型转换是需要显示使用的,所以在掌控之中的类型转换时,用C++的类型转换能够使源码的逻辑性,可读性大大提高。标准C++为了使类型转换可视化,引入了四种类型转换操作符:

static_cast

reinterpret_cast

const_cast

dynamic_cast

这些类型转换操作符的使用方法如下:

1.static_cast(对应隐式类型转换)

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

        原来可以隐式类型转换的,就可以使用static_cast进行转换。


int main()
{int a = 4;double b = static_cast<int>(a);cout << a << " " << b << endl;cout << "------------------" << endl;double d = 4.98;int f = static_cast<int>(d);cout << d << " " << f << endl;return 0;
}
4 4
------------------
4.98 4

 两种类型相差过大,根本不相关,无法类型转换:


class A
{int _a;
};
class B
{double _b;
};
int main()
{A a;B b = static_cast<A>(a);return 0;
}

2.reinterpret_cast(对应强制类型转换)

        原来需要进行强制类型转换的,需要用reinterpret_cast。

        比如:指针与整形的转换(数据的意义已经改变)

 

3.const_cast(对应强制类型转换中有风险的去掉const属性)

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

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

        当我们运行上面这一段代码,就会发现两次输出是不同的,a和*p代表的值相同,但是为什么输出的值不同?

        原因在于编译器的优化,由于识别a为const,所以直接把a的副本当作const对待了,当a改变的时候,a的副本没有改变,所以输出a(其实是a的副本)仍然是原先的2,而*p(a本身)则已经改变 为3。

        如果想要不优化,需要在a的前面加上volatile关键字即可。


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

4.dynamic_cast

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

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

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

        dynamic_cast 在运行时检查类型信息(RTTI,即运行时类型信息),以确保转换的安全性。如果转换是安全的(即基类指针或引用确实指向了一个派生类的对象),则转换成功;如果不是,则转换失败。对于指针类型的转换,失败时返回 nullptr;对于引用类型的转换,如果转换不安全,则会抛出一个 std::bad_cast 异常(但这通常不推荐用于引用类型的 dynamic_cast,因为异常可能导致程序终止)。 

注意:

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


完 ~

未经作者同意禁止转载


http://www.mrgr.cn/news/17758.html

相关文章:

  • 23种设计模式之建造者模式
  • HyperMesh教程从入门到精通:HyperMesh模型管理
  • 026、架构_资源_LoadServer
  • 在Android开发中,WiFi总是断开连接应该怎么办?
  • docker可用镜像源
  • PyTorch概述
  • iOS面试:使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?
  • 【系统架构设计师】论文:论面向服务的架构设计及其应用
  • MFC生成dll的区别
  • 新版IDEA配置前进和后退、打开资源管理器等快捷按钮
  • 【JAVA开源】基于Vue和SpringBoot的新生报到网站
  • 基于STM32设计的防盗书包(华为云IOT)(216)
  • 【Spring】Spring MVC 入门(2)
  • P2024 [NOI2001] 食物链
  • Jenkins+docker+springboot 一键自动部署项目步骤
  • 网络编程核心函数
  • Linux系统练习笔记【完整版】
  • 一起学习LeetCode热题100道(66/100)
  • LIN总线CAPL函数—— 更新特定报文数据(linUpdateResponse)
  • GMeLLo:结合知识图谱的 LLM 多跳问答技术,效果显著提升