首页 > 代码库 > 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个参数的构造函数,尽量不要使用转换操作符,而是在类中提供更好的类型转换方法成员。

不要使用枚举创建整型常量,而是使用它们创建新类型。