文章目录

        • 自动类型转换
        • string与“万物”互转
        • const char *与“万物”互转
        • char* 与”万物“互转
        • char[]与int,float,double互转
        • int,float,double互转
        • char与int
        • static_cast
        • dynamic_cast
        • const_cast
        • reinterpret_cast
        • 总结

当我们用C++编写代码时,经常会遇到数据类型的转换,如string,char*,char[],const char*、Qstring以及int,float等各种类型之间的转换。

而且有些转换的函数在低版本的C++中是不支持的,所幸这里我们对C++中常用的数据类型转换进行记录。

在数据转换中,尤其是字符串转换是最常用的,所以我们以字符串来作为整个数据类型转换的核心。

自动类型转换

这里分为两种情况:

当不同类型的变量同时运算时就会发生数据类型的自动转换。

  • char 和 int 两个类型的变量相加时,就会把 char 先转换成 int 再进行加法运算
  • 如果是 int 和 double 类型进行运算时,就会把 int 转换成 double 再进行运算。
  • 条件判断中,非布尔型自动转换为布尔类型。

用一个参数作为另一个不同类型参数的赋值时出现的自动转换。

  • 当定义参数是char,输入是int时,自动将int通过ASCII转换为字符
  • 当定义参数是int,输入是浮点型,自动转换为浮点型。

string与“万物”互转

string与const char*

//string 转 const char*string a = “niaho”;const char* b = a.c_str();//赋值//const char*转stringstring c = b; //赋值string(b) //强转

string与char*

//string 转 char*string a = “nihao”;char* c = const_cast<char*>(a.c_str());char* d =(char*)a.c_str();//采用char*强转//char* 转 stringstring(c);//强转string a = c; //赋值

string与char[]

//string 转 char[]char a[] = "nihao";string c = "oahin";//string 转 char[]//方法一for(int i=0; i<strlen(a); i++) a[i] = c[i];//方法二strcpy_s(a, c.c_str());//调用函数//char[] 转 stringstring b = a;//赋值string(a) //强转

strcpy_s是微软库的函数,如果不是vs系列的编译器,可以使用strcpy,但要注意栈溢出问题。

string与int、float、double

//string 转 int,long,doublestring a = "3.14"//方法一cout << atoi(c.c_str()) << endl;cout << atol(c.c_str()) << endl;cout << atof(c.c_str()) << endl;//方法二template<class T>T StrToNum(const string& str){istringstream iss(str);T num;iss>>num;return num;}//int,float,double 转 stringfloat a = 3.14;//方法一to_string(a);//结果 3.140000//方法二template<class T>string NumToStr(T& num){ostringstream oss;oss<<num;string str(oss.str());return str;}

这里需要提醒的是atoi函数在不同的编译器上对于溢出问题的返回是不一样的,需要注意。

to_string是C++11的标准,如果你使用的老版本C++,比如蓝桥杯比赛,是不支持to_string的。

方法二采用泛型编程的好处可以降低代码冗余而实现更多的功能。

const char *与“万物”互转

const char*与char*

//const char* 转 char*const char* a = “nihao”;char* c = const_cast<char*>(a);char* d =(char*)a;//采用char*强转//char* 转 const char*const char* f = d;//直接赋值即可cout<< (const char*)d << endl; //强转

const char*与char[]

//const char* 转 char[]char a[100];const char* s = "nihao";strcpy_s(a, s);cout<< a << endl;//char[] 转 const char*const char *s = a;//赋值

这里采用strcpy_s而不是strcpy,是因为strcpy_s操作更加的安全,不过需要采用vs code胡总和vs studio等微软的编译器才可以使用。

如果采用dev等编译器,则直接使用strcpy即可,不过需要自己注意数组溢出问题。

const char*与int, float, double

//cosnt char* 转 int,long,doubleconst char* a = "3.14"//方法一cout << atoi(a) << endl;cout << atol(a) << endl;cout << atof(a) << endl;//方法二template<class T>T StrToNum(const char* &str){istringstream iss(str);T num;iss>>num;return num;}//int,float,double 转 const char*float a = 3.14;//方法一to_string(a).c_str();//结果 3.140000//方法二template<class T>const char* NumToStr(T& num){ostringstream oss;oss<<num;const char* str = oss.str();return str;}

char* 与”万物“互转

char*与char[]

//char* 转 char[]char a[100];char* s = "nihao";strcpy_s(a, s);cout<< a << endl;//char[] 转 char*const char *s = a;//赋值

char*与int, float, double

//char* 转 int,long,doublechar* a = "3.14"//方法一cout << atoi(a) << endl;cout << atol(a) << endl;cout << atof(a) << endl;//方法二template<class T>T StrToNum(char* &str){istringstream iss(str);T num;iss>>num;return num;}//int,float,double 转 char*float a = 3.14;//方法一char* c = const_cast<char*>(to_string(a).c_str());//方法二template<class T>char* NumToStr(T& num){ostringstream oss;oss<<num;char* str = oss.str();return str;}

const只是一个常量修饰符,所以char*的很多转换操作是类似的。

其中int,float,double转char*的方法一纯纯有点傻了。

char[]与int,float,double互转

char[]与int,float,double

//char[] 转 int,long,doublechar a[] = "3.14"//方法一cout << atoi(a) << endl;cout << atol(a) << endl;cout << atof(a) << endl; //int,long,double转 char[]int a = 3;char s[100] = {0};strcpy_s(s, to_string(a).c_str());

int,float,double互转

隐式转换规则:当需求参数是int,而传入参数是float时,就会发生隐式转换,所以这里我们要注意的就是在函数重载的时候,因为隐式转换,导致编译不能有效识别部分函数。

float a = 3.14;int b = a;cout << b << endl;cout << int(b) << endl;//强转

char与int

  • 常通过ASCII表,实现字符数字与int数字之间的转换
//char 转 intchar a = '1';int b = a-'0';cout << b << endl;//int 转 charint c = 1;char d = c + '0';cout << d << endl;

上面所有的转换都是侧重于讲数据类型的”值”原封不动的转换为其他类型,而接下来的4种数据类型转换函数,则更加侧重于数据”类型“的转换。

static_cast

这个关键字的作用主要表现在 static 上,是一种静态的转换,在编译期就能确定的转换,可以完成C语言中的强制类型转换中的大部分工作,但需要注意的是,它不能转换掉表达式的 constvolitale 或者 __unaligned 属性。

{ //int 转 charint a = 124;char c = static_cast<char>(a);cout << c << endl;}{//找回存放在void*指针中的值int *a = new(int);*a = 96;void* b = static_cast<void*>(a);char* c = static_cast<char*>(b);cout << c << endl;}{ //用于父类指针 struct B { }; struct D : B { }; D d; B& rb = d; D& rd = static_cast<D&>(rb);}
  • 直接讲int转成char
  • 数据指针与指针之间的转换,这个时候不能用于两个无关指针类型的直接转换,需要通过void*进行间接转换。
  • 父类指针转成子类指针,这是安全的,反之则是不安全的,因为static_cast是没有动态类型检查的

使用时机

当编译器隐式执行类型转换时,大多数编译器会给出警告

该操作会损失精度!!

而采用static_cast可以明确告诉编译器,这是知情的情况下进行的。

dynamic_cast

只用于类继承结构中基类和派生类之间指针或引用的转换,可以进行向上、向下,或者横向的转换。

相比于 static_cast 的编译时转换, dynamic_cast 的转换还会在运行时进行类型检查,转换的条件也比较苛刻

  • 必须有继承关系的类之间才能转换
  • 在基类中有虚函数才可以,
  • 有一种特殊的情况就是可以把类指针转换成 void* 类型。
struct B { virtual void test() {} };struct D1 : virtual B { };struct D2 : virtual B { };struct MD : D1, D2 { };D1* pd1 = new MD();std::cout << pd1 << std::endl;// 向上转型B* pb = dynamic_cast<B*>(pd1);std::cout << pb << std::endl;// 向下转型MD* pmd = dynamic_cast<MD*>(pd1);std::cout << pmd << std::endl;// 横向转型D2* pd2 = dynamic_cast<D2*>(pd1);std::cout << pd2 << std::endl;

运行结果如下,在横向转换时指针发生了变化,可以看出 dynamic_cast 不是简单的数据强转,还进行了指针的偏移:

0x10619200x10619200x10619200x1061928

const_cast

const_cast 不能去除变量的常量性,只能用来去除指向常数对象的指针或引用的常量性,且去除常量性的对象必须为指针或引用。

这个我们在上面的例子中就用到了很多次,这里讲一个错误的案例

const int val = 6;//编译错误 //error: invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive]int* cp = &val;

还有就是const_cast使用往往是无奈之举,这里举一个例子,希望认真的阅读

代码1

const int val = 6;std::cout << "&val=" << &val << ", val=" << val << std::endl;const int* cp = &val;int *p = const_cast<int*>(cp);*p = 2;std::cout << "&val=" << &val << ", val=" << val << std::endl;std::cout << "p=" << p << ", *p=" << *p << std::endl;

代码2

int init = 6;const int val = init;……………………

在代码1的最前面加上了int init = 6;

运行结果如下:

&val=0x61fe0c, val=6&val=0x61fe0c, val=6p=0x61fe0c, *p=2//这里是在编译时,会对其常量值进行替换
&val=0x61fe04, val=6&val=0x61fe04, val=2//这里编译器替换已经不其作用了,而是直接更改了常量的值p=0x61fe04, *p=2 

如果可以的话,尽可能在程序设计阶段就规避这种情况,不要使用const_cast

reinterpret_cast

  • 不同类型指针或引用之间的转换。
  • 指针和整数之间的转换。

是对比特位的简单拷贝并重新解释。

不同基础类型指针类型之间转换

int *p = new int;// 编译失败 //error: invalid static_cast from type ‘int*’ to type ‘char*’char* p1 =static_cast<char*>(p);// 编译成功char* p2 =reinterpret_cast<char*>(p1);

将地址转换成整数

struct B { int val;};B b{101};std::cout << "&b=" << &b << std::endl;long addr = reinterpret_cast<long>(&b);std::cout << "addr=" << addr << std::endl;

运行结果如下:

&b=0x7ffffdc4f270addr=140737450930800

总结

综上所述,前面的数据类型”值“的转换才是本次的关键,后4个关键字我们发现大多和指针,类指针,继承甚至多态有关,这里我们重点关注数据类型之间的转换。

老规矩,有问提问,有用三连,感谢!