4.过程抽象和函数(过程抽象、函数、调试)

文章目录

  • 4.过程抽象和函数(过程抽象、函数、调试)
    • 4.1自顶向下设计
    • 4.2预定义函数
    • 4.3C++函数详解
      • 定义函数
      • 函数声明
      • 调用函数
      • 函数参数
    • 4.4C++随机数
    • 4.5重载函数名称
      • 重载入门
    • 4.6过程抽象
    • 4.7使用过程抽象
      • 前条件和后条件
    • 4.8测试和调试函数
      • 存根和驱动程序
    • 4.9常规调试技术
      • 不报成见
      • 检查常见错误
      • 定位错误
      • assert宏

4.过程抽象和函数(过程抽象、函数、调试)

4.1自顶向下设计

为了写一个程序,首先要构思出程序的用途,再用通俗易懂的文字写下这种方法。这个过程类似于拟定要办事员遵照办理的指令。这组指令称为算法
设计算法最有效的方法就是将任务分解为多个子任务,再将每个子任务分解成更小的子任务,以此类推,子任务会越变越小,很容易被C++代码实现,这种方法被称为自顶向下设计。又是也被称为 逐步求精,或者称为 分而治之

4.2预定义函数

C++提供了预定义函数库。
一些预定义函数

名称 说明 实参类型 返回值类型 示例 库的头文件
sqrt 平方根 double double sqrt(4.0) 2.0 cmath
pow 乘方 double double pow(2.0,3.0) 8.0 cmath
abs 取int值的绝对值 int int abs(-7) abs(7) 7,7 cstdlib
labs 取long值的绝对值 long long labs(-70000) 70000 cstdlib
fabs 取double值的绝对值 double double fabs(-7.5) 7.5 cmath
ceil 向上取整 double double ceil(3.2) ceil(3.9) 4.0 4.0 cmath
floor 向下取整 double double floor(3.2) floor(3.9) 3.0 3.0 cmath
srand 为随机数生成器提供种子值 srand() cstdlib
rand 随机数 int rand() 0`RAND_MAX cstdlib

4.3C++函数详解

定义函数

实例:

// 函数返回两个数中较大的那个数
 
int max(int num1, int num2) 
{
   // 局部变量声明
   int result;
 
   if (num1 > num2)
  result = num1;
   else
  result = num2;
 
   return result; 
}
  • 返回类型:一个函数可以返回一个值。如例子中,int是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,如带关键字 void的函数。
  • 函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。
  • 参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
  • 函数主体:函数主体包含一组定义函数执行任务的语句。

函数声明

函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。
如:

int max(int num1, int num2);

调用函数

当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。

函数参数

如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的形式参数。
形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。

调用类型 描述
传值调用 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。
指针调用 该方法把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
引用调用 该方法把参数的引用复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

传引用详解
程序变量作为内存位置实现,编译器为每一个变量都分配一个内存位置。在使用传引用参数时,执行该函数调用时,函数得到的不是实参的值,而是得到和每个名称关联的内存位置。因此,执行函数主体语句时,函数主体对形参执行的任何操作实际会作用于与形参关联的内存位置处的变量。

为了区分传引用参数和传值参数,在函数声明和函数定义头中,要在类型名称末尾附加符号&。
例如:

void getInput(double& fVariable)

4.4C++随机数

C++提供了生成伪随机数的预定义函数。伪随机数是表面上随机,实则由可预见的公式决定的数。

#include 
#include 
...
srand(time(0));
  • 预定义函数time(0)都返回自1970年1月1日以来经过的秒数。time函数要求必须包含ctime库。
  • srand函数为随机数生成提供种子值
  • 获取随机数则调用rand函数,它返回0`RAND_MAX的一个整数

4.5重载函数名称

重载入门

C++允许为同一个函数名称提供多个的定义,这表示在不同的情况下可以重用这些名称。例如,可以有3个名为max的函数,一个计算2个数的最大值,一个计算3个数的最大值,一个计算4个数的最大值。为同一个名称提供两个或多个函数定义被称为重载函数名称

重载函数名时,函数声明的形参必须有区别。如果两个函数唯一的区别就是返回值的类型,则不能使用重载这个函数名。

重载操作符/与重载函数名的区别,编译器已经完成了对操作符/的重载,而对函数名的重载需要依靠你的程序进行。

4.6过程抽象

只需知道程序能执行什么任务,不必知道程序是如何执行的。这就是黑盒的比喻
说一样东西是黑盒,意思是说它像一个物理设备,知道怎么使用,但内部工作原理是保密的。
有时将设计函数,使之能作为黑盒使用的过程称为信息的隐藏
函数作为黑盒来编写和使用,这也称为过程抽象
对函数主体代码的细节进行提炼(抽象)称为黑盒原则过程抽象信息隐藏

过程抽象

怎样写返回值的黑盒函数定义

  1. 在函数声明的注释中,指出对函数实参的所有要求,并指出返回值的意义
  2. 函数主体使用的变量应该在函数主体中声明

4.7使用过程抽象

过程抽象的原则在于,函数应设计成像黑盒那样使用。程序员使用函数时,只需查看函数声明和说明函数用途的注释文字,不需要知道函数主体的任何细节。

前条件和后条件

为函数声明写注释时,一个好办法就是将其分解成两种信息,即前条件和后条件。
前条件指调用函数需要满足什么条件。
后条件描述函数调用的结果。也就是说,对于要返回值的函数,后条件描述返回值。对于要对实参变量进行修改的函数,后条件描述对实参进行的修改。

4.8测试和调试函数

存根和驱动程序

每个函数都应独立于其他部分设计、编码、和测试。这是自顶向下设计策略的精髓。

  1. 函数在目标程序外部如何测试?答案是写专门的测试程序。这样的程序称为驱动程序

  2. 驱动程序是临时工具,唯一要做的就是以尽量简单的方式为函数获得参数值,然后执行函数并显示结果。

  3. 测试函数时,有时不得不用到其他尚未编写或测试的函数。这时可以使用其他函数的简化版本。称为存根函数

  4. 存根函数不必执行正确的计算,只需提供完成测试所需的值。

4.9常规调试技术

不报成见

将程序看成一个整体,不要妄图通过随意的修改代码的某些部分,以此来修正错误。

检查常见错误

  1. 未初始化的变量
  2. “相差1”错误
  3. 超过数据边界
  4. 自动类型转换
  5. 改用==的地方用了=

定位错误

  1. 可在关键位置添加cout语句打印关键变量的值,以此来跟踪变量
  2. 有时,添加大量的调试代码可能会造成新的bug。所以,许多编译器和集成开发环境都提供了一个称为调试器的独立程序,允许程序员在指定代码——称为断点——暂停执行,然后逐行执行后续代码。

assert宏

assert(断言)宏在assert语句的位置验证是否满足预期条件。条件不满足,程序显示错误并退出。

要求带有头文件#include

例如:

assert(n>0)

如果传递了任何负数或零值为参数,程序就会退出,并报告断言失败。

发布应用程序时,可能不想在最终的可执行程序中包含任何assert语句,因为用户可能看到他们不明白的错误信息。如果我们在代码中添加了大量assert语句,删除可能有一定困难。只需在程序开头,在#include 语句之前添加以下语句,即可禁用所有assert宏:

#define NDEBUG
#include 

以后修改了程序需要重新调试,删除#define NDEBUG这一行,即可重新启用所有assert宏。

你可能感兴趣的:(C++入门经典学习笔记)