C++之数据类型转换(全)

        截止C++20标准模板库同时提供了高级数值转换函数和低级数值转换函数,下面就仔细讲解一下这些数值转换函数的用法

一、数值转换

1、高级数值转换函数

        std 名称空间包含很多辅助函数,以便完成数值和字符串之间的转换,它们定义在<string>中。它们可使数值与字符串之间的相互转换更加容易。

 数值转换为字符串

        下面的函数可用于将数值转换为字符串,T可以是(unsigned) int、(signed) long、(unsigned) longlong、flat、double 以及 long double。所有这些函数都负责内存分配,它们会创建一个新的string 对象并返回。
        string to_string( T val);
        这些函数的使用非常简单直观。例如,下面的代码将 long double 值转换为字符串.
        long double d{ 3.14L );

        string s { to_string(d) };

         可以总结归纳为一个函数:

        C++11之后:

template <typename T>
std::string numConvertString(T input) {if constexpr (std::is_same<T, const char*>::value ||std::is_same<T, std::string>::value) {return input;}else {return std::to_string(input);}
}

        C++11之前:

template <typename T>
std::string  numConvertString(T value) {std::ostringstream stream;stream << value;return stream.str();
}

        字符串转换为数值

        在C++11之前可通过std::istringstream转换为数值,代码如下:

template <typename T>
T stringConvertNum(const std::string& value) {T temp;std::istringstream stream(value);stream >> temp;return temp;
}

        在C++11之后通过下面这组同样在 std 名称空间中定义的函数,可以将字符串转换为数值。在这些函数原型中,T表示要转换的字符串,idx 是一个指针,这个指针接收第一个未转换的字符的索引,base 表示转换过程中使用的进制。idx 指针可以是空指针,如果是空指针,则被忽略。如果不能执行任何转换,这些函数会抛出invalid_argument异常。如果转换的值超出返回类型的范围,则抛出out_of_range异常。

int stoi(const string& str, size t *idx=0, int base=10);
long stol(const string& str, size t *idx=0, int base=10);
unsigned long stoul(const string& str, size t*idx=0, int base=10);
long long stoll(const string& str, size t*idx-0, int base=10):
unsigned long long stoull(const string& str, size t *idx=0, int base=10);
float stof(const string& str, size t*idx=0);
double stod(const string& str, size t *idx=0);
long double stold(const string& str, size t *idx=0);

下面是一个示例:

const string toParse{"     123USD" };
size_t index { 0 };
int value{ stoi(toParse, &index) );
cout << format("Parsed value: {}",value) << endl;
cout << format("First non-parsed character: '{}'", toParse[index]) << endl;

输出如下所示:

Parsed value: 123
First non-parsed character: 'u'

        stoi()、stol()、stoul()、stoll()和 stoull()接收整数值并且有一个名为 base 的参数,表明了给定的数值应该用什么进制来表示。base 的默认值为 10,采用数字为0-9 的十进制,base为 16 表示采用十六进制。如果base 被设为0,函数会按照以下规则自动计算给定数字的进制。

  • 如果数字以0x 或者0X 开头,则被解析为十六进制数字。
  • 如果数字以0开头,则被解析为八进制数字。
  • 其他情况下,被解析为十进制数字。

2、低级数值转换

        C++也提供了许多低级数值转换函数,这些都在<charcon>头文件中定义。这些函数不执行内存分配,也不直接使用 std::string,而使用由调用者分配的缓存区。此外,它们还针对性能进行了优化并且与语言环境无关。最终的结果是,这些函数可以比他高级数值转换函数快几个数量级。这些函数也是为所谓的完美往返而设计的,这意味着将数值序列化为字符串表示,然后将结果字符串反序列化为数值,结果与原始值完全相同。

        如果希望实现高性能、完美往返、独立于语言环境的转换,则应当使用这些函数。例如,在数数据与人类可读格式(如JSON、XML等)之间进行序列化/反序列化。

        数值转换为字符串
        将整数转换为字符,可使用下面一组函数。

   to_chars_result to_chars(char* first, char* last, Integer value, int base = 10);

        这里,IntegerT 可以是任何有符号或无符号的整数类型或char 类型。结果是 to_chars_result 类型类型定义如下所示。
        struct to_chars_result {
                        char* ptr;
                        errc ec;
                };

        如果转换成功,pt 成员将等于所写入字符尾后一位置的指针。如果转换失败(即 ec==errc::value_too_large),则它等于last。

       下面是一个使用示例:

const size t BufferSize{ 50 };
string out(BufferSize,' '); // A string of BufferSize space characters.
auto result { to_chars (out.data(), out.data() + out,size(),12345) };
if (result.ec == errc()) { cout << out << endl; /* Conversion successful,*/ }

   使用结构化绑定,可以将其写成:

string out (BufferSize, ' ');
auto [ptr,error] { to_chars(out,data(),out.data() + out,size(),12345) };
if (error == errc{}) {cout << out << endl; }

   类似地,下面的一组转换函数可用于浮点类型。

to chars_result to chars(char* first,char* last,FloatT value);
to_chars_result tochars(char* first,char* last,FloatT value,chars_format format);
to_chars_result to_chars(char* first, char* last,FloatT value,chars_format format, int  precision);

        这里,FloatT可以是float、double 或long double。可使用 chars_format 标志的组合指定格式:   

enum class chars_format {scientific,   // style:(-)d.dddetddfixed,        // style:(-)ddd.dddhex,       // style: (-)h.hhhptd (Note: no 0x!)general = fixed I scientific   // See next paragraph.};

        默认格式是 chars_format::general,这将导致 to_chars()将浮点值转换为(-)dddddd形式的十进制表示形式,或(-)d.dddedd形式的十进制指数表示形式,得到最短的表示形式,小数点前至少有一位数字(如果存在)。如果指定了格式,但未指定精度,将为给定格式自动确定最简短的表示形式,最大精度为6个数字。例如:

double value { 0.314 }; 
string out (BufferSize, ' ' );// A string of BufferSize space characters.
auto [ptr, error] { to_chars(out.data(), out.data()+ out.size(), value) };
if (error == errc{})  cout << out << endl; /* Conversion successfiullue) )

        字符串转换为数值

        对于相反的转换,即将字符串转换为数值,可使用下面的一组函数。

from_chars_result   from_chars(const char* first, const char* lastIntegerTs value,int base = 10);
from_chars_result   from_chars(const char* first, const char* last.FloatTs value,chars_format format = chars_format::general)

        fom_chars_result的类型定义如下:
                struct from_chars_result {
                                const char* ptr;
                                errc ec;
                };

        结果类型的ptr成员是指向第一个未转换字符的指针,如果所有字符都成功转换,则它等于 last。如果所有字符都未转换,则ptr 等于 first,错误代码的值将为errc::invalid argument。如果解析后的值过大无法由给定类型表示,则错误代码的值将是 errc::result_out of range。注意,from_chars()不会忽略任何前导空白。
        to_chars()和form_chars()的完美往返特性可以表示如下:

double value1{ 0.314 }; 
string out (BufferSize, ' ' );// A string of BufferSize space characters.
auto [ptr1, error1] { to_chars (out.data(), out.data() + out.size(), value1) };
if (error1 == errc{}) { cout << out << endl; /* Conversion successful*/ }double value2; 
auto [ptr2, error2] { to_chars (out.data(), out.data() + out.size(), value2) };
if (error2 == errc{}) {if (value1== value2){cout << "perfect roundtrip" << endl;} else{cout << "No perfect roundtrip?!?" << endl;}
}

二、数据类型转换

1、static_cast

        语法:static_cast<type-name>(expression)
         仅当 type-name 可以隐式转换为 expression 所属的类型,或者 expression 可以隐式转换为 type-name 所属的类型,转换才是合法的。否则,编译器会报错。
         可以将有继承关系的派生类对象的地址赋给基类指针。即使基类中没有虚函数也可以使用 static_cast 进行转换。
         可以将有继承关系的基类对象的地址赋给派生类指针。因为派生类指针可以隐式转换为基类指针,无需显式类型转换,所以可以用  static_cast 进行另一个方向的转换,即将基类指针转换为派生类指针。但是,这样做有什么意义呢?
         同理,因为枚举值可以隐式转换为整型,无需显式类型转换,所以可以用 static_cast 将整型转换为枚举类型。
         如果将没有继承关系的对象的地址赋给另一个类的指针,编译器会报错。

示例:

class ITestBase {};
class CDerived : public ITestBase {};
class CMyClass {};
void  main() {//[1]float x = 15.52;int y = x; // C like castint z = static_cast<int>(x);cout >> "trans z: " >> z;  //输出:trans z: 4//[2]CDerived* d = new CDerived;ITestBase* b = static_cast<ITestBase*>(d); // this line will work properly//CMyClass* c = static_cast<CMyClass*>(d); // ERROR will be generated during compilationCDerived* f = static_cast<CDerived*>(b); // 可以正确转换return 0;
}

2、const_cast

        语法:const_cast <type_name> (expression)
        const_cast运算符用于执行只有一种用途的类型转化,即改变const或volatile。这里我们需要强调的是 const_cast主要用于更改指针或引用的const或volatile限定符。其中,type_name必须是指针、引用或者成员指针类型。

        示例1:

    //未定义的行为,不提倡使用const int j = 3; // 声明 j 为 constint *pj = const_cast<int *>(&j);*pj = 4; // 未定义行为std::cout << "j = " << j << " ,addr(j):" << &j << '\n';std::cout << "*pj = " << *pj << " ,addr(*pj):" << pj << '\n';//正常的行为int j1 = 3;//最初声明为非constconst int *cpj1  = &j1;int *pj1 = const_cast<int *>(cpj1);//cpj1最终指向的值(即j1的值)为非const类型,可以使用const_cast*pj1 = 4;std::cout << "j1 = " << j1 << " ,addr(j1):" << &j1 << '\n';std::cout << "*pj1 = " << *pj1 << " ,addr(*pj1):" << pj1 << '\n';

        示例2:

void func(const int& a)//形参为,引用指向const int
{int& b = const_cast<int&>(a);//去掉const限定,因为原本为非常量b++;return;
}int main()
{int a = 100;func(a);cout << a << endl;  // 打印101return 0;
}

        使用const_cast去掉const限定符,只有当对象原本就是非常量时,才是正确的行为。

3、dynamic_cast

        语法:dynamic_cast <newType> (expression)

        dynamic_cast 用于在类的继承层次之间进行类型转换,它既允许向上转型(Upcasting),也允许向下转型(Downcasting)。向上转型是无条件的,不会进行任何检测,所以都能成功;向下转型的前提必须是安全的,要借助 RTTI 进行检测,所有只有一部分能成功。
        dynamic_cast 与 static_cast 是相对的,dynamic_cast 是“动态转换”的意思,static_cast 是“静态转换”的意思。dynamic_cast 会在程序运行期间借助 RTTI 进行类型转换,这就要求基类必须包含虚函数;static_cast 在编译期间完成类型转换,能够更加及时地发现错误。
        newType 和 expression 必须同时是指针类型或者引用类型。换句话说,dynamic_cast 只能转换指针类型和引用类型,其它类型(int、double、数组、类、结构体等)都不行。
对于指针,如果转换失败将返回 NULL;对于引用,如果转换失败将抛出std::bad_cast异常。

示例:

#include <iostream>
#include <string>using namespace std;class Base
{  
//有虚函数,因此是多态基类
public:virtual ~Base() {}
};class Derived : public Base { };int main()
{Base b;Derived d;Derived* pd;pd = reinterpret_cast <Derived*> (&b);if (pd == NULL)//此处pd不会为 NULL。reinterpret_cast不检查安全性,总是进行转换cout << "unsafe reinterpret_cast" << endl; //不会执行pd = dynamic_cast <Derived*> (&b);if (pd == NULL)  //结果会是NULL,因为 &b 不指向派生类对象,此转换不安全cout << "unsafe dynamic_cast1" << endl;  //会执行pd = dynamic_cast <Derived*> (&d);  //安全的转换if (pd == NULL)  //此处 pd 不会为 NULLcout << "unsafe dynamic_cast2" << endl;  //不会执行return 0;
}

4、reinterpret_cast

        语法:reinterpret_cast <newType> (expression)

        reinterpret_cast 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换,reinterpret_cast 转换时,执行的过程是逐个比特复制的操作。
        这种转换提供了很强的灵活性,但转换的安全性只能由程序员的细心来保证了。例如,程序员执意要把一个 int* 指针、函数指针或其他类型的指针转换成 string* 类型的指针也是可以的,至于以后用转换后的指针调用 string 类的成员函数引发错误,程序员也只能自行承担查找错误的烦琐工作:(C++ 标准不允许将函数指针转换成对象指针,但有些编译器,如 Visual Studio 2010,则支持这种转换)。

下面的代码代码演示了 reinterpret_cast 的使用:

#include <iostream>
using namespace std;class B{
public:B(int a = 0, int b = 0): m_a(a), m_b(b){}
private:int m_a;int m_b;
};int main(){//将 char* 转换为 float*char str[]="http://c.biancheng.net";float *p1 = reinterpret_cast<float*>(str);cout<<*p1<<endl;      //输出: 3.0262e+29//将 int 转换为 int*int *p = reinterpret_cast<int*>(45);//将 B* 转换为 int*p = reinterpret_cast<int*>(new B(11, 12));cout<<*p<<endl;  //输出: 25return 0;
}

        可以想象,用一个 float 指针来操作一个 char 数组是一件多么荒诞和危险的事情,这样的转换方式不到万不得已的时候不要使用。将B*转换为int*,使用指针直接访问 private 成员刺穿了一个类的封装性,更好的办法是让类提供 get/set 函数,间接地访问成员变量。

参考

https://cplusplus.com/reference/sstream/ostringstream/

https://cplusplus.com/reference/sstream/istringstream/

《C++20高级编程》

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

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

相关文章

测试开发体系介绍——测试体系介绍-L3

目录&#xff1a; 测试框架体系TDDDDTBDDATDD介绍 测试框架是什么&#xff1f;测试框架的价值&#xff1a;测试框架的收益&#xff1a;常见测试框架类型&#xff1a;TDDBDDBehaviorDrivenDevelopmentATDDAcceptanceTestDrivenDevelopmentMBTModelBasedTestingDDTDataDrivenTes…

案例147:基于微信小程序的酒店管理系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

Golang 泛型实现原理

文章目录 1.有 interface{} 为什么还要有泛型&#xff1f;2.泛型实现原理2.1 类型参数泛型函数泛型数据结构 2.2 类型约束2.3 编译时生成虚拟方法表单态化 Go 的实现 3.小结参考wenxian 泛型&#xff08;Generics&#xff09;是 Go 语言在较早版本缺失的一个特性&#xff0c;直…

MFC 视图窗口

目录 视图窗口概述 视图窗口的使用 视图窗口创建流程 命令消息 WM_COMMAND 处理顺序 对象关系 视图窗口概述 作用&#xff1a;提供了一个用于显示数据的窗口 关于视图窗口 视图类是用来展示用户&#xff0c;文档类是用来存储和管理数据视图窗口是覆盖掉框架窗口的客户区…

vue的插槽解析

插槽 好处&#xff1a;组件的内容结构可定制 用slot插槽进行占位 语法: 子组件中通过slot进行占位 理解&#xff1a;父组件&#xff0c;在子组件标签嵌套的内容就会被渲染到slot地方 一、默认插槽 //子组件 <slot>slot插槽</slot> //方法一<slot name"…

qt,滚动条,放大缩小拖动图片

头文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QLabel> #include <QWheelEvent> #include <QMouseEvent> #include <QtDebug>#include <math.h> #include <QPainter> #include <QTimer>…

详解Vue3中的插槽(slot)

本文主要介绍Vue3中的插槽&#xff08;slot&#xff09;。 目录 一、在普通写法中使用插槽&#xff08;slot&#xff09;作用域插槽默认插槽 二、在setup写法中使用插槽&#xff1a;注意事项 在Vue3中&#xff0c;插槽&#xff08;slot&#xff09;是一种用于在父组件中向子组件…

Unity矩阵平移旋转缩放Matrix4x4

Unity矩阵平移旋转缩放Matrix4x4 Unity中的矩阵&#xff08;Matrix4x4&#xff09;创建自定义模型平移矩阵缩放矩阵旋转矩阵 Unity中的矩阵&#xff08;Matrix4x4&#xff09; 最近在研究帧同步定点数物理系统中需要自定义定点数矩阵&#xff0c;所以在这里分享下基础的矩阵案…

蓝牙物联网在智能家居中的应用前景

物联网智能家居系统是应用物联网技术&#xff0c;在传统家居环境下将各种零散无序的电器整合成统一整体&#xff0c;实现家电的全程自动控制&#xff0c;满足用户高效管理需求的一种新型家居模式。 其主要的子系统有家居感知系统、家庭网络系统、智能家居控制管理系统等&#x…

使用教程之【SkyWant.[2304]】路由器操作系统,破解移动【Netkeeper】校园网【小白篇】

许多高校目前饱受Netkeeper认证的痛苦&#xff0c;普通路由器无法使用&#xff0c; 教你利用SkyWant的Netkeeper认证软件来使你的SkyWant路由器顺利认证上网&#xff0c;全宿舍又可以合作共赢了&#xff01; 步骤一&#xff1a;正确连接网线&#xff0c;插电开机 正确连接网…

SQLITE如何同时查询出第一条和最后一条两条记录

一个时间记录表&#xff0c;需要同时得到整个表或一段时间内第一条和最后一条两条记录&#xff0c;按如下方法会提示错误&#xff1a;ORDER BY clause should come after UNION not before select * from sdayXX order by op_date asc limit 1 union select * from sday…

分布式Session使用步骤

目录 1. 为什么用分布式Session2. Spring-Session使用步骤2-1. 添加依赖2-2. yml配置 1. 为什么用分布式Session 将一个项目部署到多台服务器上时&#xff0c;多台服务器的Tomcat的Session不共享。那么就有可能造成Session数据不一致情况&#xff0c;所以此时就需要使用分布式…

Gateway API

Gateway API 目录 原文链接 https://onedayxyy.cn/docs/GatewayAPI 本节实战 实战名称&#x1f6a9; 实战&#xff1a;Gateway API在istio里的安装及测试-2023.12.23(测试失败) 前言 Gateway API 是由 SIG-NETWORK 社区管理的开源项目&#xff0c;项目地址&#xff1a;http…

Java设计模式-单例模式(Singleton)

Java中实现单例模式有几种不同的方式,每种方式都有其特点和适用场景。下面是两种常用的实现方式:懒汉式和饿汉式。 懒汉式(线程安全) 懒汉式单例是指在第一次被引用时才会创建实例。为了确保线程安全,可以使用同步方法或同步块。 public class SingletonLazy {private sta…

微信小程序生成一个天气查询的小程序

微信小程序生成一个天气查询的小程序 基本的页面结构和逻辑 页面结构&#xff1a;包括一个输入框和一个查询按钮。 页面逻辑&#xff1a;在用户输入城市名称后&#xff0c;点击查询按钮&#xff0c;跳转到天气详情页面&#xff0c;并将城市名称作为参数传递。 主要代码 index…

nodejs+vue+ElementUi洗衣店订单管理系统4691l

衣服但是找订单的时间太长&#xff0c;体验非常的差。而且对于店家这也很头疼&#xff0c;麻烦的查找订单的方式&#xff0c;让他总是重复着繁琐的步骤&#xff0c;记录的时候也很容易出问题&#xff0c;容易把衣服弄错&#xff0c;再然后就是对于收来的衣服也很麻烦&#xff0…

数据结构:线性表顺序存储结构———顺序表

目录 顺序表的定义 初始化线性表 销毁线性表 求线性表的长度 判断是否为空表 插入数据元素 逻辑序号与物理序号的区别 删除数据元素 输出线性表 按序号求线性表中的元素 按元素值查找 整体上创建顺序表 顺序表实验 线性表的顺序存储是把线性表中的所有元素按照其逻…

初识QT(上篇):What Qt

初识QT&#xff08;上篇&#xff09;&#xff1a;What Qt 前言 & 说明前言说明 初识QT1.1 QT的what1. 介绍2. 发展历程3. QT架构的主要内容4.QT的常用模块 1.2 QT的 why1. QT的核心机制 下篇笔记链接 前言 & 说明 前言 前言&#xff1a; 之前说要share的qt相关知识&am…

log4j rename方法

log4j日志切割 os.rename [rootzz test]# cat a2.py import os os.rename(a.txt,b.txt); [rootzz test]# cat a.txt 111111111111111111111 222222222222222222222 [rootzz test]# ls a1.py a2.py a.txt tst.log.1 tst.log.2 [rootzz test]# python ^C [rootzz test]# s…

【AI提示词人物篇】创新艺术未来,让科技改变想象空间

AI 绘画学习难度和练习技巧 学习绘画的技巧 学习能难度&#xff1a; 外貌特征&#xff1a;AI需要学习识别和理解各种外貌特征&#xff0c;如发型、肤色、眼睛颜色等。这可能需要大量的训练数据和复杂的模型架构。 镜头提示&#xff1a;AI需要学习理解不同镜头提示的含义&…