声明:老样子,侵权或者理解不到位的地方,请后台通知我们。本公众号致力于从最基础的知识点入手,尽力搞清楚每一个知识点背后的真相,让大伙告别“两行代码,三个bug, 五个warning”的尴尬局面。
今天主要解决两个问题:
什么是隐式转换
什么是临时对象
1. 隐式转换
我们简单点说就是编译器“偷偷的”帮你把一个数据类型转换成另外一个数据类型的转换:
1.内置类型:
比如说:int A =1; float B = 1.1;float C =A+B;
以上例子中就有一个隐式转换,编译器把A先转换成一个float类型的临时对象,然后在用这个临时对象与B相加赋值给C。当不同类型的内置类型数据做运算的时候,隐式转换规则如下:
以上转换如果做静态代码检测应该是会报出warning的,所以我们在做计算的时候可以告诉显示的告诉编译器,这种转换是可以接受的,这个时候可以显示的进行转换,如上面可以写成: float C =(float)A+B。
注意:对于内置类型的转换(显示或者隐式)要注意以下两个方面:
显示将整型从高字节转成低字节,注意上限丢失。
float类型与整型之间的转换,如果有浮点保护,注意转换是否满足个芯片对于浮点保护的规则。
2.类对象隐式转换
大家都见过,在只有一个形参的构造函数前面(对于那些有N个参数,但是又N-1个已经又默认值的构造函数也属于这一类),我们经常会加一个explicit关键字。如(下面是不加explicit的情况):
class test:
{
test(int A){} //一个参数
test(&test, int A = 0){} //两个参数,但是有一个已经默认了
}
当没有explicit的时候,我们看下以下代码:
test B = 1;我的天,这个时候你可能要吐槽了,会不会写代码,一个int类型怎么能用来初始化一个类对象? 但实际编译的时候就是没报错。
于是我们加上explicit:
class test:
{
explicit test(int A){} //一个参数
explicit test(&test, int A = 0){} //两个参数,但是有一个已经默认了
}
test B = 1再来个一样的代码,怎么回事,这次编译器报错了!
这就是另外一个我们不常关注的隐式转换的点:
对于只有一个参数的构造函数,编译器会将对应参数类型的数据"偷偷的"转换成类对象。也就是说在没有用到explicit的时候,对于test A = 2,编译器做了以下事情:
调用与参数类型相同的构造函数,产生一个临时对象. test temp(2).
然后调用拷贝构造函数将temp赋值给A。
当用到explicit之后:
我们必须显示的去调用构造函数,否则编译器就会报错。
有了这个explicit之后我们对拷贝构造函数的“操作权”就牢牢掌握在自己手里了,一旦发现有“隐式转换”的“不规范”代码,编译器就会报错。
2. 临时对象
老样子,用最直白的表达来解释看似很难懂的专业词汇,临时对象简单点理解就是代码开发者无形中给了编译器一个暗示,让他“偷偷的”给你创造一个临时对象。
无形中意味着如果没有关注过,开发者永远都不知道自己做了什么,让编译器产生了这种要产生对象的错觉。
就好比我们上面的例子:test A =2, 你永远也不知道实际已经有个temp对象被生成了。
那么什么时候会产生临时对象呢?笔者总结了以下几点:
以值传递的形参,传进函数的形参实际上是在栈上的实参的一个拷贝。对形参做任何改变都不会影响实参的值。
像我们上一节说的隐式类型转换
return一个类对象的时候会在栈上构造一个临时对象。
非堆上定义的一个无名对象, 如test &Aref=test(3)这个引用实际上绑定的是个临时对象。
因为临时对象的产生既影响软件运行效率(可能需要调用构造函数),又占用内存。所以在我们开发的过程中,大伙也需要对这个问题稍微有所了解。特别说明:临时对象是要考虑生存期的,就好比假设我们用一个引用来指向一个return出来的临时对象,return语句一结束,临时对象就没了,这个时候就是个悬空引用了,之后在去用这个引用就会引发一些不可预估的行为(当然对于这种错误笔者相信编译的时候应该会有相应的warning会报出来吧???)。对于这种生存期的问题我们在代码中切记留个心眼。
笔者还未弄清的问题?
1.临时对象分全局和局部的吗?就好比说我在全局写:test A = 2, 和在函数内部写有什么区别?如果分,那么全局和局部的临时对象的生存期分别是多久?
笔者猜测:针对 test A =2, 无论全局还是局部在临时对象作为参数初始化对象A之后,这个temp对象应该会立即“消失”。对于全局的,会被编译器优化,对于局部的A的构造函数运行起来之后,temp作为参数完成其任务之后就会从栈上消失。但是函数运行起来之后每个函数都有自己的栈空间,函数没有return的话,栈空间也不会消失啊?所以temp应该要等到函数return之后才会消失吧????
2.return的值怎么返回到调用函数的局部变量?
如:int func (){int B=1;return B;}
void main() { int A = func;}
A的赋值是func函数的栈空间被释放前还是释放后?这个赋值过程怎么理解?
笔者猜测1:return的时候B的值被存在某个寄存器,然后func的栈空间被释放,函数返回到赋值语句,赋值语句将寄存器存的B的值给变量A?但是对于类对象,对于返回临时大数组元素(暂且不考虑代码规范问题),这个猜测不成立,寄存器可能放不下这么大的元素。
笔者猜测2:return之后func的栈空间没有立马释放,而是将赋值语句执行之后才释放,对于小元素直接将栈上的值赋给main空间变量,对于大的原色(如大数组)返回一个指针和长度,然后执行mempy??
对于以上两个问题,可能是笔者还有很多基础指示不扎实,希望寻求答案!!!!