C++中的各式类型转换

news2025/5/14 9:42:46

隐式转换:

基本类型的隐式转换:

当函数参数类型非精确匹配,但是可以转换的时候发生

如:

void func1(double x){
    cout << x << endl;
}

void func2(char c){
    cout << c << endl;
}

int main(){
    func1(2);//int隐式转换为double
    func2(3);//int隐式转换为char
    return 0;
}

自定义类型的隐式转换:

函数参数列表接受类类型变量,传入的单个参数不是类对象,但可以通过这个参数调用类对应的构造函数,那么编译器就会将其隐式转换为类对象

如:

class A{
private:
    int a;
public:
    A(int x)
        :a{x}
    {}
};
void func(A a){
    cout << "func" << endl;
}

int main(){
    func(1);//隐式将int转换为A对象
    A obj = 1;//同上
    return 0;
}

多参数构造函数:

调用多参数构造函数时,将所有参数用用{}括起来,表示是一个初始化列表,但这并不属于自定义类型隐式转换,而是隐式构造

class A{
private:
    int a;
public:
    A(int x)
        :a{x}
    {}
};

class B{
private:
    A obj;
    int b;
public:
    B(A a,int x)
        :obj{a}
        ,b{x}
    {}
};

void func(B b){
    cout << "func" << endl;
}
int main(){
    func({A{1}, 2});//使用{A,int}隐式构造B对象
    func({1,2});//仍能运行,隐式将1转换为A对象,然后用{A,int}隐式构造B对象
    return 0;
}

值得注意:

自定义的类型转换不能连续发生两次以上,否则会报错

例如:

class A{
private:
    string str;
public:
    A(const string& s)
        :str{s}
    {}
};

void func(A a){
    cout << "func"<<endl;
}

int main(){
    func("Hello");//报错,因为需要将const char*隐式转换为string,string转换为const string,
                  //再将string隐式转换为A
                  //需要发生两次自定义类型隐式转换
    func(string{"Hello"});//编译通过,只需要string转换为const string,再将string隐式转换为A,
                          //只发生一次自定义类型隐式转换
    return 0;
}

禁止任何隐式调用的关键字:explicit

在类构造函数前加上explicit关键字,则该构造函数无法再被隐式转换或隐式构造

从而防止某些错误的函数调用,比如想传入int却被隐式转换为了类对象

class A{
public:
    explicit A(int x){
        cout << x << endl;
    }
    explicit A(int x,int y){
        cout << x << y << endl;
    }
};

A func(int a,int b){
    return {a, b};//禁止{int,int}到A的隐式构造
}

A func(int x){
    return A{x};//合法
    return A{x, x};//合法
}

int main(){
    A a = 1;//禁止int到A的隐式转换
    return 0;
}
好习惯:

将所有接受单个参数(包括多个参数,但只有一个参数没有缺省值的)的构造函数都限定为explicit

拷贝构造和移动构造不应该限定为explicit

显式转换:

初始化列表式转换:

使用type{var}的方式进行转换,如int{'c'}

这种转换只允许小范围变量向大范围变量转换,不允许反向,如

int x{3.5};//不被允许截断
long long y{3};
cout<<double{y}<<endl;//不允许被截断

C风格转换:

使用类似(int),或者int()的方式来转换类型,底层是组合调用了C++的各种cast函数来进行转换,因此不推荐使用这种转换

在这种转换中,尝试的cast调用顺序:

const_cast

static_cast

static_cast+const_cast

reinterpret_cast

reinterpret_cast+const_cast

const_cast转换:

实际极少使用

使用方式:

const_cast<to_type>(var)

作用:

给变量添加/去除const属性

如果该变量本身就是const变量,则无法真正地去除const属性

例如:

int main(){
    const int x = 2;
    const int *p = &x;
    int *p2 = const_cast<int *>(p);
    *p2 = 3;
    cout << x << endl;
    cout << *p2 << endl;
    return 0;
}

输出:

2
3

这里我们试图去除p所指向的的变量的const属性,并通过p2来修改其值,但在打印x时输出的值仍为2,这是因为编译器在编译的时候知道x为const变量,因此将cout<<x直接优化成了cout<<2,从而保证了其不会被修改。

而我们尝试使用p2去修改x的值,这属于未定义行为,需避免。因此,对于本身为const属性的变量,我们不应该使用const_cast来去除其const属性,否则可能会导致未定义行为

const_cast用于去除本来不是const变量的const属性

如:

void func(const int*p){
    int *p2 = const_cast<int *>(p);//用来去除被函数传参过程中隐式添加的const属性
    *p2 = 6;
}

int main(){
    int x = 3;
    const int *p = const_cast<const int *>(&x);//人为为其添加const属性,使其无法被修改
    //在经过了一段时间的应用后,现在又想修改x的值了
    int *p2 = const_cast<int *>(p);
    *p2 = 5;
    cout << *p << endl;
    func(&x);
    cout << *p << endl;
    return 0;
}

static_cast转换:

使用方式:

static_cast<to_type>(var)

作用:

1.进行基本类型之间的转换(允许截断,告诉编译器是有意为之的截断)

相比之下,列表初始化转化不允许截断,因此更推荐使用static_cast进行转换,当然程序员要负起截断的责任,告诉大家这是有意为之的截断,而不是自己大意导致的截断

int x=static_cast<int>(3.14);

2.进行安全性检查,不允许无关类型指针互转

int main(){
    double x = 3.2;
    int *p2 = static_cast<int *>(&x);//不合法
    return 0;
}

3.可以将void*指针转为任意类型指针

无安全性检查,需要保证指向的类型正确(下例为不正确用法)

int main(){
    double x = 3.2;
    int *p2 = static_cast<int *>(static_cast<void*>(&x));
    //通过double*转void*,再从void*转到int*,跳过了安全性检查
    return 0;
}

4.将派生类指针/引用/对象转换为基类指针/引用/对象(向上转换)

class Base{
};
class Derived:public Base{
};
int main(){
    Base *p_b = static_cast<Base *>(new Derived);
    Derived d;
    Base &ref_b = static_cast<Base &>(d);
    Base obj_b = static_cast<Base>(d);//导致对象切片
}

5.将基类指针/引用转换为派生类指针/引用(向下转换,不推荐)

无安全性检查,需要保证基类指针指向要转换的派生类或者其派生类

若基类派生类拥有虚函数,应优先使用dynamic_cast(下文讲解)

Base* p_b2=new Derived;
Derived* p_d=static_cast<Derived*>(p_b2);//合法

6.显式调用自定义构造函数/类型转换函数进行转换

class MyInt {
public:
  explicit MyInt(int x) : value(x) {}
  operator int() const { return value; }  // 自定义的转换运算符
private:
  int value;
};

MyInt mi = static_cast<MyInt>(42);  // 调用构造函数
int x = static_cast<int>(mi);       // 调用 operator int()

dynamic_cast转换:

使用方式:

dynamic_cast<Derived_ptr/Derived_ref>(Base_ptr/Base_ref);

作用:

仅能对拥有虚函数的基类/派生类使用,且不能是protected/private继承关系

基类为虚基类时,在某些情况会导致dynamic_cast失败

对基类的指针/引用进行安全检查的向下转换(转换为派生类指针/引用)

也可进行向上转换(不推荐,应使用static_cast)

返回值:

若转换成功,即Base_ptr/Base_ref实际指向的对象是Derived对象或者是其派生类对象,则返回指向该对象的Derived_ptr/Derived_ref

若转换失败,即Base_ptr/Base_ref实际指向的是别的东西,则返回nullptr

值得注意的是:

dynamic_cast依赖于RTTI(run-time type information)来确定指针指向对象的实际类型从而进行转换,因此,没有虚函数,或者关闭了RTTI优化,都会导致对象的RTTI信息丢失,从而导致dynamic_cast失败

reinterpret_cast转换:

该转换无安全性检查,直接重新解释对象的二进制比特模式

高安全风险,极少使用,不推荐

使用方式:

reinterpret_cast<to_type>(var);

作用:

1.任意类型之间的指针互转

2.指针与整数互转

3.函数指针与数据指针互转

4.任意数据类型互转

路径不确定导致的转换二义性:

1.非虚继承的菱形继承的向上转换:

此时将指向D对象的D指针向上转换为A指针时,会出现二义性错误,因为编译器不知道指向哪个A对象,这种情况下,只能一层一层地向上转换,直到产生二义性的路径消失,方可一次性转换到A

2.虚继承导致的菱形继承的向下转换:

此时将指向E对象的A指针向下转换为B指针时,会出现二义性错误,因为编译器不知道指向哪个B对象,而且虚继承导致指针偏移无法计算(虚继承机制以后的文章再讲解),这种情况下,只能先转成E指针,再向上转换,直到二义性路径消失

3.同层交叉cast转换:

可以直接将指向E对象的D指针用dynamic_cast转换为B指针

只需要满足B和D没有共同的虚基类即可(有的话,会导致指针偏移无法计算,从而dynamic_cast失败),有的话,使用第二点的方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2375271.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nacos源码—9.Nacos升级gRPC分析七

大纲 10.gRPC客户端初始化分析 11.gRPC客户端的心跳机制(健康检查) 12.gRPC服务端如何处理客户端的建立连接请求 13.gRPC服务端如何映射各种请求与对应的Handler处理类 14.gRPC简单介绍 10.gRPC客户端初始化分析 (1)gRPC客户端代理初始化的源码 (2)gRPC客户端启动的源码…

【计算机视觉】基于深度学习的实时情绪检测系统:emotion-detection项目深度解析

基于深度学习的实时情绪检测系统&#xff1a;emotion-detection项目深度解析 1. 项目概述2. 技术原理与模型架构2.1 核心算法1) 数据预处理流程2) 改进型MobileNetV2 2.2 系统架构 3. 实战部署指南3.1 环境配置3.2 数据集准备3.3 模型训练3.4 实时推理 4. 常见问题与解决方案4.…

【图像处理基石】什么是油画感?

在图像处理中&#xff0c;“油画感”通常指图像呈现出类似油画的块状纹理、笔触痕迹或色彩过渡不自然的现象&#xff0c;表现为细节模糊、边缘不锐利、颜色断层或人工纹理明显。这种问题常见于照片处理、视频帧截图或压缩后的图像&#xff0c;本质是画质受损的一种表现。以下是…

AD PCB布线的常用命令

PCB布线顺序&#xff1a;先信号&#xff0c;再电源&#xff0c;再GNG 1.多根走线的应用 将IC上的引脚分类 更改一类引脚以及引线的颜色&#xff0c;画出走线&#xff08;将脚引出&#xff09; 选中这些走线&#xff0c;点击‘交互式总线布线’&#xff0c;便可以多根拉线 shi…

【3-2】HDLC

前言 前面我们提到了 PSTN&#xff08;Public Switched Telephone Network&#xff09; &#xff0c;今天介绍一种很少见的数据链路层的协议&#xff0c;HDLC&#xff01; 文章目录 前言1. 定义2. 帧边界3. 零比特填充4. 控制字段4.1. 信息帧&#xff08;I帧&#xff09;4.2. …

MySQL 学习(八)如何打开binlog日志

目录 一、默认状态二、如何检查 binlog 状态三、如何开启 binlog3.1 临时开启&#xff08;重启后失效&#xff09;3.2 永久开启&#xff08;需修改配置文件&#xff09;3.3 验证是否开启成功3.4 查看 binlog 内容 四、高级配置建议五、注意事项六、开启后的日常维护 知识回顾&a…

OpenCV进阶操作:光流估计

文章目录 前言一、光流估计1、光流估计是什么&#xff1f;2、光流估计的前提&#xff1f;1&#xff09;亮度恒定2&#xff09;小运动3&#xff09;空间一致 3、OpenCV中的经典光流算法1&#xff09;Lucas-Kanade方法&#xff08;稀疏光流&#xff09;2&#xff09; Farneback方…

4. 文字效果/2D-3D转换 - 3D翻转卡片

4. 文字效果/2D-3D转换 - 3D翻转卡片 案例&#xff1a;3D产品展示卡片 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><style type"text/css">.scene {width: 300px;height…

【AI News | 20250513】每日AI进展

AI Repos 1、iap-diffusion-labs 从零开始带我们构建完整的扩散模型。通过三个精心设计的实验练习&#xff0c;循序渐进地引导我们实现流匹配和扩散模型&#xff0c;从基础 SDE 到条件图像生成&#xff0c;每一步都有详尽指导和完整代码&#xff0c;让复杂理论简单易懂。主要内…

mybatisplus 集成逻辑删除

一开始&#xff0c;没去查资料&#xff0c;后面要被AI气死了&#xff0c;先看它的的话 一开始&#xff0c;看ai的描述&#xff0c;我还以为&#xff0c;不需要改数据库&#xff0c;mybatis-puls自动拦截集成就可以实现逻辑删除&#xff0c;c&#xff0c;最后还是要给数据库加一…

SimScape物理建模实例2--带控制的单质量弹簧阻尼系统

模型下载&#xff1a; 基于simscape&#xff0c;单质量系统带位置控制资源-CSDN文库 在实例1中&#xff0c;我们搭建了不带控制的单质量弹簧阻尼系统&#xff0c;该系统没有外界力量介入&#xff0c;只有弹簧的初始弹力&#xff0c;带着弹簧使劲弹来弹去。 SimScape物理建模实…

PyGame游戏开发(含源码+演示视频+开结题报告+设计文档)

前言&#xff1a; 大二小学期python课上基于pygame做的一个游戏小demo&#xff0c;当时老师花了一天讲解了下python基础语法后&#xff08;也是整个大学四年唯一学习python的时间&#xff09;&#xff0c;便让我们自学网课一周然后交项目&#xff0c;所以做的非常仓促&#xff…

拒绝flash插件打劫!如何在vscode上玩4399小游戏

现在电脑上玩4399都需要flash插件了 这也导致了很多人无法玩到小时候的游戏 今天介绍一款插件 功能强大 即安即玩 首先打开vscode 点开小方框&#xff08;拓展&#xff09;搜索4399 认准4399 on vscode点击安装 安装完毕后 按下 Ctrl Shift P , 输入 4399 on VSCode 或…

learning ray之ray核心设计和架构

我们每天都在处理海量、多样且高速生成的数据&#xff0c;这对计算能力提出了前所未有的挑战。传统的单机计算模式在面对日益复杂的机器学习模型和大规模数据集时&#xff0c;往往显得力不从心。更重要的是&#xff0c;数据科学家们本应专注于模型训练、特征工程、超参数调优这…

C语言while循环的用法(非常详细,附带实例)

while 是 C 语言中的一种循环控制结构&#xff0c;用于在特定条件为真时重复执行一段代码。 while 循环的语法如下&#xff1a; while (条件表达式) { // 循环体&#xff1a;条件为真时执行的代码 } 条件表达式&#xff1a;返回真&#xff08;非 0&#xff09;或假&#x…

JavaScript进阶(九)

第三部分:JavaScript进阶 目录 第三部分:JavaScript进阶 一、作用域 1.1 局部作用域 1. 作用域 2. 局部作用域 函数作用域 块作用域 1.2 全局作用域 1.3 作用域链 1.4 JS垃圾回收机制 1. 什么是垃圾回收机制 2. 内存的声明周期 3. 垃圾回收的算法说明 引用计数…

数据结构与算法分析实验11 实现顺序查找表

实现顺序查找表 1.上机名称2.上机要求3.上机环境4.程序清单(写明运行结果及结果分析)4.1 程序清单4.1.1 头文件4.1.2 实现文件4.1.3 源文件 4.2 实现展效果示 上机体会 1.上机名称 实现顺序查找表 顺序查找表的基本概念 顺序查找表是一种线性数据结构&#xff0c;通常用于存储…

获取高德地图JS API的安全密钥和Key的方法

要使用高德地图JavaScript API&#xff0c;您需要获取API Key和安全密钥(securityJsCode)。以下是获取步骤&#xff1a; 1. 注册高德开放平台账号 首先访问高德开放平台&#xff0c;如果没有账号需要先注册。 2. 创建应用获取Key 登录后进入"控制台" 点击"应…

JAVA研发+前后端分离,ZKmall开源商城B2C商城如何保障系统性能?

在电商行业竞争白热化的当下&#xff0c;B2C 商城系统的性能表现成为决定用户留存与商业成败的关键因素。ZKmall 开源商城凭借 Java 研发与前后端分离架构的深度融合&#xff0c;构建起一套高效、稳定且具备强大扩展性的系统架构&#xff0c;从底层技术到上层应用全方位保障性能…

嵌入式自学第二十天(5.13)

&#xff08;1&#xff09;线性表顺序存储的优缺点&#xff1a; 优点&#xff1a;无需为表中逻辑关系添加额外存储空间&#xff1b; 可以快速随机访问元素&#xff0c;时间复杂度O(1)。 缺点&#xff1a;插入删除需要移动元素O(n&#xff09;&#xff1b; 无法动态存储。 …