c++隐式类型转换问题
前言:
上次的多线程系列因为某些原因暂时停了,后续会根据实际遇到的问题做一些补充。写博客是为了记录我遇到的问题以及解决,便于自我总结,也帮助遇到这些问题的人。
言归正传,本文介绍了c++以及c风格的强制类型转换的一些注意点。
一、c风格强制类型转换
- 问题描述: 我在做报文解析的时候遇到这个问题:
float x=32.128f;
float tmp=x*1000;
unsigned int ret=(unsigned int)tmp; //不安全转换,不推荐
这个看起来没啥问题,我当时预测是32128,但最后控制效果是32127,损失了一些精度。
2. 先说结论: 这得说到浮点数在内存中的存储,科学计数法,
符号位 + 1.XXXXXX(xxx是数据位)*2^ooooooo(o是指数位)
也就是说,float x=32.128f
实际上在内存中以科学计数法表示,只能做到尽可能近似,而不会完全相等。
此处的x
实际值可能是32.1279971
当x*1000之后,值为32127.9971
,然后强制转化为unsigned int ,暴力的把小数点后面给干掉了,导致结果为321227
3. 解决方法:
~避免使用这种类型转换,它是不安全的。
~如果一定要使用的话,请使用double
,原因如下
double x=32.218;
实际上x=32.1280000000000000000012
简而言之,就是double的数据位数更多,可以更准确的表达x的值,从而减少运算造成的精度损失
4. 顺带一提:
~double等类型数据是否为0的判断:
double x;
if((x-0)<1e-10) //也是科学计数法的存储只能很接近原始数据的原因
~不同的机器对float的精度不一样,我用自己的电脑复现那个问题,需要把x*1000000,(可能我的精度比较高吧)
二、c++隐式转换问题浅谈
关于c++强制类型转换问题,主要对static_cast<>
,看一段代码:代码中有注释不同类型,可根据需要选择浏览
#include<iostream>
using std::cout;
using std::endl;
class Parent
{
public:
Parent(int x=0) :data_p(x) {} //构造
void show()
{
cout << "parent: data_p=" << data_p << endl;
}
protected:
int data_p;
};
class Son :public Parent
{
public:
Son(int ag = 0, int data = 0) :Parent(data), age(ag) {}
void show()
{
cout << "son: adrs_age="<<&age << endl;
cout << "son: adrs_data_p="<<&data_p << endl;
}
private:
int age;
};
class A //测试int转class的类
{
private:
int data;
double s;
public:
A(int d) :data(d) { s = 12.3; cout << "A() with one parameter" << endl; } //前面加explicit可避免此情况
A(int d,double ss) :data(d),s(ss) { cout << "A()with two parameters" << endl; }
A(const A& t) :data(t.data),s(t.s) { cout <<" A(const A&t)" << endl; }
void show()
{
cout << "A: data="<<data << endl;
cout << "A: s=" << s<< endl;
}
};
int main()
{
//1.基本数据类型之间的转换(int double 之类的),就不说啦。
//2.基类子类之间的转换
//2.1对象转换
Parent p;
Son s;
cout << sizeof(s) << endl; //转换前后,字节一样
s.show(); //转换前后,数据没被截断
Parent tmp1 = static_cast<Parent>(s); //把子类转换成基类
cout << sizeof(s) << endl;
s.show();
//Son tmp2 = static_cast<Son>(p); //禁止基类向子类转换
cout << "我是分界线-------------------------------->" << endl;
//2.2指针转换
Parent *pp =new Parent;
Son *ps =new Son;
Parent* tmp3 = static_cast<Parent*>(ps);//子类指针转为基类
tmp3->show();
//Son* tmp4 = static_cast<Son>(pp); //禁止基类向子类转换
delete pp;
delete ps;
cout << "我是分界线---------------------------->" << endl;
//3.隐式转换,例如从int转换为class ,或者struct
int a_data = 23;
A a = static_cast<A>(a_data); //int转换为类A,调用一个参数的构造函数
a.show();
return 0;
}
- 基本数据类型转换:
- 子类和基类之间的转换:子类指针可以转基类指针,基类不能转子类(用dynamic_cast可以,但是转换后,子类的部分数据未知,不可控)。子类对象转基类是可以的,但是一般使用指针。总结,向上转换安全。
- 如果类的构造函数的参数只有一个
变量
(常量字符串不算),那么就会发生类似int--------->calss
的隐式转换,要避免这种转换,可以再构造函数前面申明explicit
- 结构体我再Ubuntu上测试也是可以转换
运行结果图如下:
结语:
关于c++4种类型转换的使用,不在说啦,如有错误,欢迎指正。