文章目录
- 自动类型转换
- 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*转string
string c = b; //赋值
string(b) //强转
- 1
- 2
- 3
- 4
- 5
- 6
- 7
string与char*
//string 转 char*
string a = “nihao”;
char* c = const_cast<char*>(a.c_str());
char* d = (char*)a.c_str(); //采用char*强转
//char* 转 string
string(c); //强转
string a = c; //赋值
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
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[] 转 string
string b = a; //赋值
string(a) //强转
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
strcpy_s是微软库的函数,如果不是vs系列的编译器,可以使用strcpy,但要注意栈溢出问题。
string与int、float、double
//string 转 int,long,double
string 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 转 string
float 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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
这里需要提醒的是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; //强转
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
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; //赋值
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这里采用strcpy_s而不是strcpy,是因为strcpy_s操作更加的安全,不过需要采用vs code胡总和vs studio等微软的编译器才可以使用。
如果采用dev等编译器,则直接使用strcpy即可,不过需要自己注意数组溢出问题。
const char*与int, float, double
//cosnt char* 转 int,long,double
const 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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
char* 与”万物“互转
char*与char[]
//char* 转 char[]
char a[100];
char* s = "nihao";
strcpy_s(a, s);
cout<< a << endl;
//char[] 转 char*
const char *s = a; //赋值
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
char*与int, float, double
//char* 转 int,long,double
char* 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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
const只是一个常量修饰符,所以char*的很多转换操作是类似的。
其中int,float,double转char*的方法一纯纯有点傻了。
char[]与int,float,double互转
char[]与int,float,double
//char[] 转 int,long,double
char 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());
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
int,float,double互转
隐式转换规则:当需求参数是int,而传入参数是float时,就会发生隐式转换,所以这里我们要注意的就是在函数重载的时候,因为隐式转换,导致编译不能有效识别部分函数。
float a = 3.14;
int b = a;
cout << b << endl;
cout << int(b) << endl; //强转
- 1
- 2
- 3
- 4
char与int
- 常通过ASCII表,实现字符数字与int数字之间的转换
//char 转 int
char a = '1';
int b = a-'0';
cout << b << endl;
//int 转 char
int c = 1;
char d = c + '0';
cout << d << endl;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
上面所有的转换都是侧重于讲数据类型的"值"原封不动的转换为其他类型,而接下来的4种数据类型转换函数,则更加侧重于数据”类型“的转换。
static_cast
这个关键字的作用主要表现在 static
上,是一种静态的转换,在编译期就能确定的转换,可以完成C语言中的强制类型转换中的大部分工作,但需要注意的是,它不能转换掉表达式的 const
、volitale
或者 __unaligned
属性。
{ //int 转 char
int 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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 直接讲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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
运行结果如下,在横向转换时指针发生了变化,可以看出 dynamic_cast
不是简单的数据强转,还进行了指针的偏移:
0x1061920
0x1061920
0x1061920
0x1061928
- 1
- 2
- 3
- 4
const_cast
const_cast
不能去除变量的常量性,只能用来去除指向常数对象的指针或引用的常量性,且去除常量性的对象必须为指针或引用。
这个我们在上面的例子中就用到了很多次,这里讲一个错误的案例
const int val = 6;
//编译错误 //error: invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive]
int* cp = &val;
- 1
- 2
- 3
- 4
还有就是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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
代码2
int init = 6;
const int val = init;
……………………
- 1
- 2
- 3
- 4
在代码1的最前面加上了int init = 6;
运行结果如下:
&val=0x61fe0c, val=6
&val=0x61fe0c, val=6
p=0x61fe0c, *p=2 //这里是在编译时,会对其常量值进行替换
- 1
- 2
- 3
&val=0x61fe04, val=6
&val=0x61fe04, val=2 //这里编译器替换已经不其作用了,而是直接更改了常量的值
p=0x61fe04, *p=2
- 1
- 2
- 3
如果可以的话,尽可能在程序设计阶段就规避这种情况,不要使用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);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
将地址转换成整数
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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
运行结果如下:
&b=0x7ffffdc4f270
addr=140737450930800
- 1
- 2
总结
综上所述,前面的数据类型”值“的转换才是本次的关键,后4个关键字我们发现大多和指针,类指针,继承甚至多态有关,这里我们重点关注数据类型之间的转换。
老规矩,有问提问,有用三连,感谢!