//C++测试与调试知识 #ifndef OutputRoots_ #define OutputRoots_ template<class T> void OutputRoots(T a,T b,T c) //计算并输出一个二次方程的根 { float d=b*b-4*a*c; if(d>0) { //两个实根 float sqrtd=sqrt(d); std::cout<<"有两个实根" <<(-b+sqrtd)/(2*a) << " and " <<(-b-sqrtd)/(2*a) << std::endl; } else if(d==0) { //两个实根相同 std::cout<<"有一个唯一实根" <<-b/(2*a)<<std::endl; } else { //复数根 std::cout<<"复数根" <<std::endl <<"实数部分是" <<-b/(2*a)<<std::endl <<"虚数部分是" << sqrt(-d)/(2*a)<<std::endl; std::cout<<"即为"<<-b/(2*a)<<" + "<<sqrt(-d)/(2*a)<<"i"<<std::endl; } }; template<class T> void ComputeRootsRightMethod(T x,T a,T b,T c) { std::cout<<a<<"X*X+("<<b<<")*X+"<<c<<"=0"<<std::endl; std::cout<<"f(x,a,b,c)值为"<<a*x*x+b*x+c<<std::endl; } //OutputRoots(1,-5,6); out 2,3 (x^2-5x+6=0) //OutputRoots(1,3,2); out -1,-2 (x^2+3x+2=0); /* 什么是测试: ---- 所谓程序测试是指在目标计算机上利用输入数据, 也称之为测试数据( test data)来实际运行该程序, 把程序的实际行为与所期望的行为进行比较。 如果两种行为不同,就可判定程序中有问题存在。 ---- 正确性是一个程序最重要的属性。 由于采用严格的数学证明方法来证明一个程序的正确性是非常困难的 (哪怕是一个很小的程序), 所以我们想转而求助于程序测试(program test)过程来实施这项工作; ---- 然而,不幸的是,即使两种行为相同,也不能够断定程序就是正确的, 因为对于其他的测试数据,两种行为又可能不一样。 如果使用了许多组测试数据都能够看到这两种行为是一样的, 我们可以增加对程序正确性的信心。通过使用 所用可能的测试数据,可以验证一个程序是否正确。 然而,对于大多数实际的程序,可能的测试数据的数量太大了, 不可能进行穷尽测试, 实际用来测试的输入数据空间的子集称之为测试集(test set) ---- 设计测试数据 ---- 在设计测试数据的时候,应当牢记:测试的目标是去披露错误。 如果用来寻找错误的测试数据找不到错误,我们就可以有信心相信程序的正确性。 为了弄清楚对于一个给定的测试数据,程序是否存在错误, 首先必须知道对于该测试数据,程序的正确结果应是什么。 ---- 可以采用下面的条件来计算任何候选的测试数据: 1这个数据能够发现错误的潜力如何? 2能否验证采用这个数据时程序的正确性? ---- 设计测试数据的技术分为两类: 黑盒法(black box method)(最流行的黑盒法是I/O 分类及因果图) 白盒法(white box method)(白盒法基于对代码的考察来设计测试数据) ---- 对一个测试集最起码的要求就是使程序中的每一条语句都至少执行一次。这种要求被称为语句覆盖 在分支覆盖(decision coverage)中要求测试集要能够使程序中的每一个条件都分别能出现true和false两种情况。 ---- */ template<class T> int Max(T a[],int n) { int pos=0; for(int i=1;i<n;i++) if(a[pos]<a[i]) pos=i; return pos; } //测试数据集a[0:4]=[2,4,6,8,9] 能语句覆盖但不能分支覆盖 //测试数据集a[0:4]=[4,2,6,8,9] 能语句覆盖也能分支覆盖 //---- /*可以进一步加强分支覆盖的条件,要求每个条件中的每个从句(clause)既能出现true也能 出现false的情况,这种加强的条件被称之为从句覆盖(clause coverage) ---- 在这些白盒测试方法中,一般要求实现执行路径覆盖。一个能实现全部执行路径覆盖的测 试数据同样能实现语句覆盖和分支覆盖,然而,它可能无法实现从句覆盖。全部执行路径覆盖 通常会需要无数的测试数据或至少是非常可观的测试数据,所以在实践中一般不可能进行全部 执行路径覆盖。 调试(debug) ------------- 测试能够发现程序中的错误。一旦测试过程中产生的结果与所期望的结果不同,就可以了 解到程序中存在错误。确定并纠正程序错误的过程被称为调试(debug)。 ---- 1可以用逻辑推理的方法来确定错误语句。如果这种方法失败,还可以进行程序跟踪,以 确定程序什么时候开始出现错误。如果对于给定的测试数据程序需要运行很多指令,因而需要 跟踪太多语句,很难人工确定错误,此时,这种方法就不太可行了,在这种情况下,必须试着 把可疑的代码分离出来,专门跟踪这段代码。 2不要试图通过产生异常来纠正错误。异常的数量可能会迅速增长。必须首先找到需要纠 正的错误,然后根据需要重新设计。 3在纠正一个错误时,必须保证不会产生一个新的、以前没有的错误。用原本能使程序正 确运行的测试数据来运行纠正过错误的程序,确信对于该数据,程序仍然正确。 4在测试和调试一个有错的程序时,从一个与其他函数独立的函数开始。这个函数应该是 一个典型的输入或输出函数。然后每次引入一个尚未测试的函数,测试并调试更大一些的程序。 这种策略被称为增量测试与调试( incremental test and debug)。在使用这种策略时,可以有理 由认为产生错误的语句位于刚刚引入的函数之中。 */ #endif // Win32Console.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "baseCplusplus1.h"; #include "baseClassRelInfo2.h"; #include "TestProgram.h" void baseCplusplusOp(); //C++函数模板递归一二维数组动态分配存储空间实例1 void baseClassRelInfo2(); //C++类的相关基础2 void baseClassRelInfo2_1(); //C++类的相关基础2 //操作符重载 void ComputeRoots(); //计算根公式 void findMax(); //寻找最大值 int _tmain(int argc, _TCHAR* argv[]) { baseCplusplusOp(); baseClassRelInfo2(); std::cout<<"/操作符重载"<<std::endl; baseClassRelInfo2_1(); std::cout<<"计算根公式"<<std::endl; ComputeRoots(); std::cout<<"寻找最大值"<<std::endl; findMax(); //暂停操作 char str; std::cin>>str; //程序结束 return 0; } //计算根公式 void ComputeRoots() { OutputRoots(1,-5,6); //out 2,3 ComputeRootsRightMethod(2,1,-5,6); OutputRoots(1,3,2); //out -1,-2 OutputRoots(1,-3,2); //out 2,1 OutputRoots(4,4,1); //out 0 OutputRoots(1,-8,16); //out 4 OutputRoots(1,2,5); //out -1+2i } //寻找最大值 void findMax() { int a[]={2,4,6,8,9}; std::cout<<Max(a,5)<<std::endl; int aa[]={4,2,6,9,8}; std::cout<<Max(aa,5)<<std::endl; }