首页 > 代码库 > C++调试之缺陷捕捉
C++调试之缺陷捕捉
时间:2014.06.25
地点:基地
-----------------------------------------------------------------------------
一、在编译时捕捉缺陷
捕捉缺陷可在编译时发生也可在运行时发生,只要有可能,我们应该尽力在编译时捕捉缺陷。
原因:
1.在编译期检测到的缺陷,给出的是一条文本信息,正确描述了所发生的错误是什么,发生在哪以及发生的位置。
2..完整的编译完整地覆盖了程序中的所有代码,即若编译器没有返回错误或警告,即可相信程序中百分之百的不存在编译时可检测到的错误。(这点对于运行时测试 就无法保证了,当代码庞大时,很难保证所有分支都被测试到,也无法保证程序每行代码都会至少执行一次,即无法保证代码测试100%的覆盖,且即便同一段代码,对于不同的输入,测试结果也可能不一样)
3.编译时捕捉到的错误可以节省程序维护时间,早发现早超生(运行时错误难以复制和跟踪)
基于这样一些等等的原因,我们应该尽量在编译器捕捉到程序缺陷,于是我们在编写代码时,应该能使程序具有自我诊断功能,方便在编译器寻找缺陷。
-----------------------------------------------------------------------------
二、正确处理类型
问题描述
有时,我们编写一个函数处理一些自定义数据类型,比如我们已经定义好两个类型:类Apple和类Orange,现有函数声明如下:
void DoSomeWithOrange(const Orange& orange);如果我们在调用DoSomeWithOrange时,不小心传入了Apple对象,如下:
Apple an_apple(some_inputs); DoSomeWithOrange(an_apple);很多场合下,这样的误传类型的代码是可以编译通过的,因为编译器总是试图尽可能地帮你进行类型转换,使之满足要求,即Apple可能在这偷偷地转换为Orangte类型,我们并不期待这种转换,于是有必要在编译器就检测到这样的失误。
发生这么偷偷的类型转换有两种情况:
a.Orange类具有一个只接受Apple类型参数的构造函数。如下:
class Orange { public: Orange(const Apple& apple); }; 或 class Orange { publick: Orange(const Apple& apple,const Banana* p_banana=0); };像上面这样的类,在调用需要Orange类的地方,如果误传实参Apple类型,Apple类型就会发生这种偷偷的类型转换。
class Orange { public: explicit Orange(const Apple& apple); };或
class Orange { publick: explicit Orange(const Apple& apple,const Banana* p_banana=0); };这样,可以防止编译器执行隐式自动类型转换,使得在需要期望接受Orange的地方就必须使用Orange了。
b.当然,当提供一个转换操作符将Apple类型对象显式转换为Orange类型,也会发生这种偷偷的隐式类型转换
class Apple { public: //...... operator Orange() const; };
解决办法是,我们为Apple类提供了转换为Orange的显式转换操作符
class Apple { public: //...... Orange AsOrange() const; };于是,这样,我们调用函数时可如下调用:
Apple apple(some_inputs); DoSomethingWithOrange(apple.AsOrange()); //显式转换
-----------------------------------------------------------------------------
三、使用枚举类型
先定义如下两个枚举,分别表一周中的星期和一年的月份
enum{SUM,MON,TUE,WED,THU,FRI,SAT}; enum{JAN=1,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC};我们知道,这些实际上都是整数,当我们有一个期望接受一周中的星期作为参数的函数时:
void FuncExpectingDayOfWeek(int day_of_week);现在,假如我们误传了一年中的某个月编译也将顺利通过:
FuncExpectionDayOfWeek(JAN);在这里因为星期和月份的表示所以编译顺利通过,为了避免在编译时就捕捉在这种误传,我们应该使用创建新类型的枚举
typedef enum{SUM,MON,TUE,WED,THU,FRI,SAT}DayOfWeek; enum{JAN=1,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC}Month; void FuncExpetingDayOfWeek(DayOfWeek day_of_week);现在,当传 JAN时将会发生编译错误。
总的一点说来,大多数情况我们应该尽量避免隐式类型转换,这就允许我们充分利用编译器检查不同变量类型的功能,在早期编译时捕捉到潜在的错误。但也总有一些错误只能在运行时被检测得到,那也是不可避免的,所以只是尽量在编译器检测到错误。
-----------------------------------------------------------------------------
四、总结
禁止隐式类型转换:用关键字explicit声明接受1个参数的构造函数,尽量不要使用转换操作符,而是在类中提供更好的类型转换方法成员。
不要使用枚举创建整型常量,而是使用它们创建新类型。