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

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失败),有的话,使用第二点的方法

相关文章:

  • vr视频制作攻略(VR视频制作基础知识)
  • PNG图片转icon图标Python脚本(简易版) - 随笔
  • 43、Server.UrlEncode、HttpUtility.UrlDecode的区别?
  • dockers笔记
  • 3.4 数字特征
  • LeetCode LCR 016. 无重复字符的最长子串 (Java)
  • centos7.x下,使用宝塔进行主从复制的原理和实践
  • JavaScript实践(三)JavaScript序列化与反序列化深度解析
  • 使用ADB命令操作Android的apk/aab包
  • PyTorch 分布式训练
  • 2025年渗透测试面试题总结-渗透测试红队面试九(题目+回答)
  • Milvus(21):过滤搜索、范围搜索、分组搜索
  • 【2025最新】Pycharm里如何运行多个py文件
  • Python基础学习-Day23
  • 撤回不了一点 v1.0.2,支持微信QQ钉钉飞书等消息防撤回
  • yolo11n-obb训练rknn模型
  • 博客系统技术需求文档(基于 Flask)
  • ArcGIS、InVEST与RUSLE在水土流失模拟及分析中的实践技术
  • 使用docker安装clickhouse集群
  • K230 ISP:一种新的白平衡标定方法
  • 车载抬头显示爆发在即?业内:凭借市场和产业链优势,国内供应商实现反超
  • 外交部:中方对美芬太尼反制仍然有效
  • 《克莱默夫妇》导演罗伯特·本顿去世,终年92岁
  • 京东美团饿了么等外卖平台被约谈
  • 秦洪看盘|交易新逻辑,银行股成A股稳定器
  • 法治课|争议中的“行人安全距离”于法无据,考量“注意义务”才更合理