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

C++中的各式类型转换

隐式转换:

基本类型的隐式转换:

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

如:

void func1(double x){cout << x << endl;
}void func2(char c){cout << c << endl;
}int main(){func1(2);//int隐式转换为doublefunc2(3);//int隐式转换为charreturn 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.xdnf.cn/news/5850.html

相关文章:

  • 序列化和反序列化(hadoop)
  • RabbitMQ发布订阅模式深度解析与实践指南
  • 解决 CentOS 7 镜像源无法访问的问题
  • 爬虫请求频率应控制在多少合适?
  • cocos creator 3.8 下的 2D 改动
  • Kubernetes Horizontal Pod Autosscaler(HPA)核心机制解析
  • 【android bluetooth 框架分析 02】【Module详解 6】【StorageModule 模块介绍】
  • C#进阶(1) ArrayList
  • TDengine编译成功后的bin目录下的文件的作用
  • 【计算机组成原理】第二部分 存储器--分类、层次结构
  • Altium Designer AD如何输出PIN带网络名的PDF装配图
  • 智能意图识别 + 内容定位,contextgem重构文档处理逻辑
  • ExoPlayer 如何实现音画同步
  • 记录为什么LIst数组“增删慢“,LinkedList链表“查改快“?
  • 信息学奥赛一本通 1535:【例 1】数列操作
  • 新一代动态可重构处理器技术,用于加速嵌入式 AI 应用
  • WSL 安装 Debian 12 后,Linux 如何安装 vim ?
  • OpenVLA (2) 机器人环境和环境数据
  • 【UAP】《Empirical Upper Bound in Object Detection and More》
  • 【HTML5】【AJAX的几种封装方法详解】
  • 【deekseek】TCP Offload Engine
  • LeetCode 648 单词替换题解
  • Baklib智能云平台加速企业数据治理
  • 桑德拉精神与开源链动2+1模式AI智能名片S2B2C商城小程序的协同价值研究
  • 01.类型转换+Scanner+制表符嫦娥例题
  • dockers笔记
  • FastDDS Transport功能模块初步整理
  • 《医院网络安全运营能力成熟度评估指南》(试行版)研究解读
  • Spring Boot 的自动配置为 Spring MVC 做了哪些事情?
  • matlab多智能体网络一致性研究