编写高质量代码——提防隐式转换带来的麻烦

在C/C++ 语言,允许在不同类型的数据之间进行某一操作或混合操作,如果类型不同,则要将数据转换成相同的数据类型(隐式转换和显示转换)。

=========================

隐式转换主要发生的情形:

▉基本类型之间的隐式转换

C/C++ 中规定的两个通用转换原则:

1)为防止精度损失,类型总是被提升为较宽的类型。

2)所有含有小于整数类型的算术表达式在计算之前其类型都被转换成整形。

对于C++最直接的害处是:可能导致 重载函数 产生二义性。

例如:

void Print(int inval);

void Print(float fval);

int ival = 2;

float fval = 2.0f;

Print(ival); // OK, int-version

Print(fval); // OK, float-version

Print(1); //OK, int-version

Print(0.5); //ERROR ! !

T* 指针到 void* 的隐式转换

▉non-explict constructor 接受一个参数的用户定义类对象之间隐式转换:

class A{

public:

    A(int x):m_data(x){}

private:

   int m_data;

};

void DoSomething(A aObject);

DoSomething(20);

------------------------------------------------------------------

调用DoSomething() 函数时,实参和形参类型不一致,但是类A的构造函数含有一个 int 类型的参数,编译器会以 20 为参数调用A的构造函数,以便构造临时对象,然后传给 DoSomething() 函数。

------------------------------------------------------------------

当无法完成类似上述的直接隐式转换时,编译器会尝试间接的方式:

void DoSomething(A aObject);

float fval = 20.0f;

DoSomething(fval);

------------------------------------------------------------------

Note:1)隐式转换一个误用也许会引起难以捉摸的错误

2)隐式转换过程中需要调用类的构造函数、析构函数,如果这种转换代价很大,那么这样的隐式转换将会影响系统性能。

------------------------------------------------------------------

相对安全的隐式转换:子类到基类的隐式转换 和const 到 non-const 的同类型隐式转换

=========================

控制隐式转换的两条有效途径:

●使用具名转换函数:

用 operator as_T() 替代 operator T()(T 为 C++ 数据类型)

●使用 explict 限制的构造函数(只针对有一个单参数构造函数的用户自定义类型)

class Widget{

public:

    Widget( unsigned int factor );

    Widget( const char* name, const Widget* other = NULL );

};//
在只有一个参数时,unsigned int 和 const char* 参数类型的数据均可隐式转换为自定义的 Widget 类型

控制以上隐式转换的方法:为单参数的构造函数加explicit关键字

class Widget{

public:

explicit

Widget( unsigned int factor );

explicit

Widget( const char* name, const Widget* other = NULL );

};

编写高质量代码——提防隐式转换带来的麻烦

时间: 2024-10-29 10:45:50

编写高质量代码——提防隐式转换带来的麻烦的相关文章

编写高质量代码,改善C++程序的150个建议:指针、初始化和运算符

建议0:不要让main函数返回void 首先C++ 标准中从没有出现过void main(){}这样的函数定义. 标准的主函数定义有两种: int main() int main(int argc,char * argv[]) 在main函数中,return 语句的作用在于离开main函数(析构掉所有具有动态生存时间的对象),并将其返回值作为参数来调用exit函数.如果函数执行到结尾儿没有遇到return 语句,其效果就等于执行了return 0. 建议1:区分0 的四种面孔 1)整形0.作为一

编写高质量代码改善C#程序的157个建议——建议47:即使提供了显式释放方法,也应该在终结器中提供隐式清理

建议47:即使提供了显式释放方法,也应该在终结器中提供隐式清理 在标准的Dispose模式中,我们注意到一个以~开头的方法,如下: /// <summary> /// 必须,防止程序员忘记了显式调用Dispose方法 /// </summary> ~SampleClass() { //必须为false Dispose(false); } 这个方法叫做类型的终结器.提供类型终结器的意义在于,我们不能奢望类型的调用者肯定会主动调用Dispose方法,基于终结器会被垃圾回收这个特点,它被

编写高质量代码改善C#程序的157个建议——建议8: 避免给枚举类型的元素提供显式的值

建议8: 避免给枚举类型的元素提供显式的值 一般情况下,没有必要给枚举类型的元素提供显式的值.创建枚举的理由之一,就是为了代替使用实际的数值.不正确地为枚举类型的元素设定显式的值,会带来意想不到的错误. 如果为建议7中的枚举类型Week增加一个元素,代码如下所示: enum Week { Monday = 1, Tuesday = 2, ValueTemp, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7 } 设

编写高质量代码改善C#程序的157个建议——建议46:显式释放资源需继承接口IDisposable

建议46:显式释放资源需继承接口IDisposable C#中的每一个类型都代表一种资源,资源分为两类: 托管资源:由CLR管理分配和释放的资源,即从CLR里new出来的对象. 非托管资源:不受CLR管理的对象,如Windows内核对象,或者文件.数据库连接.套接字.COOM对象等. 如果我们的类型使用了非托管资源,或者需要显示地释放托管资源,那么就需要让类型继承接口IDisposable,这毫无例外.这相当于告诉调用者,类型资源是需要显示释放资源的,你需要调用类型的Dispose方法. 一个标

编写高质量代码——“零星”总结

不要让main函数返回void //在C++中绝对没有出现过 void main(){  }这样的函数定义,在C语言中也是. //两种 main 的定义方式:int main( void ); //                     int main( int argc, char** argv ) //第一版的C语言中只有 int 一种数据类型,为了兼容 需要,不明确标明返回值的,默认为 int //在main函数中,return 语句的作用在于离开main函数(析构掉所有具有动态生存时

编写高质量代码——运算符重载,是成员函数还是友元函数

一.运算符重载的四项基本原则: ▍不可臆造运算符. ▍运算符原有操作数的个数.优先级和结合性不能改变. ▍操作数中至少一个是自定义类型. ▍保持运算符的自然含义. ============================== 二.运算符重载的两种形式: ▍成员函数形式(隐含一个参数 this 指针): 1)双目运算符:参数一个 2)单目运算符:不能显示的声明参数 ▍友元函数形式(不存在隐含的参数 this 指针) 1)双目运算符:两个参数 2)单目运算符:一个参数 ===============

编写高质量代码改善C#程序的157个建议——建议5: 使用int?来确保值类型也可以为null

建议5: 使用int?来确保值类型也可以为null 基元类型为什么需要为null?考虑两个场景: 1)数据库中一个int字段可以被设置为null.在C#中,值被取出来后,为了将它赋值给int类型,不得不首先判断一下它是否为null.如果将null直接赋值给int类型,会引发异常. 2)在一个分布式系统中,服务器需要接收并解析来自于客户端的数据.一个int型数据可能在传输过程中丢失或被篡改了,转型失败后应该保存为null值,而不是提供一个初始值. 类似的场景还有很多,所以从.NET 2.0开始,F

编写高质量代码改善C#程序的157个建议——建议2: 使用默认转型方法

建议2: 使用默认转型方法 除了字符串操作外,程序员普遍会遇到的第二个问题是:如何正确地对类型实现转型.在上一个建议中,从int转型为string,我们使用了类型int的ToString方法.在大部分情况下,当需要对FCL提供的类型进行转型时,都应该使用FCL提供的转型方法. 这些转型方法包括: 使用类型的转换运算符. 使用类型内置的Parse.TryParse,或者如ToString.ToDouble和ToDateTime等方法. 使用帮助类提供的方法. 使用CLR支持的转型. 下面分别对这些

[ 转 ]编写高质量代码:改善Java程序的151个建议

记得3年前刚到公司,同桌同事见我无事可做就借我看<编写高质量代码:改善Java程序的151个建议>这本书,当时看了几页没上心就没研究了.到上个月在公司偶然看到,于是乎又找来看看,我的天,真是非常多的干货,对于我这种静不下心的人真是帮助莫大呀. 看完整本书,也记了不少笔记,我就分享一部分个人觉得有意义的内容,也为了方便以后自己温习. --警惕自增陷阱 i++表示先赋值后自增,而++i表示先自增后赋值.下面的代码返回结果为0,因为lastAdd++有返回值,而返回值是自增前的值(在自增前变量的原始