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

当我们用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)   //强转

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; //赋值

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)   //强转

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;
}

这里需要提醒的是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,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;
}
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,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;
}

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());
int,float,double互转

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

float a = 3.14;
int b = a;
cout << b << endl;
cout << int(b) << endl;  //强转
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;

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

static_cast

这个关键字的作用主要表现在 static 上,是一种静态的转换,在编译期就能确定的转换,可以完成C语言中的强制类型转换中的大部分工作,但需要注意的是,它不能转换掉表达式的 constvolitale 或者 __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);
}
  • 直接讲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 不是简单的数据强转,还进行了指针的偏移:

0x1061920
0x1061920
0x1061920
0x1061928
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=6
p=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=0x7ffffdc4f270
addr=140737450930800
总结

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

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

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>