第六章 函数

第六章 函数

函数调用

  1. 调用函数需要先声明函数原型;

  2. 若函数定义在调用点之前,可以不另外声明;

  3. 若函数定义在调用点之后,必须要在调用函数前声明函数原型:

  4. 函数原型:类型标识符 被调用函数名(含类型说明的形参表)
    注意:实参的调用(形实结合过程)才是函数中的形参的实际初始化。

函数的递归调用

例题 1:用递归法计算从n个人中选择k个人组成一个委员会的不同组合数
分析:由n个人里选k个人的组合数 = 由n-1个人里选k个人的组合数 + 由n-1个人里选k-1个人的组合数。
当n = k 或 k = 0 时,组合数为1。

源代码:

#include <iostream>
using namespace std;

int comm(int n, int k)
{
      if (k > n) return 0;
      else if (n == k || k == 0) return 1;
      else return comm(n - 1, k) + comm(n - 1, k - 1);
}

int main()
{
       int  n, k;
       cin >> n >> k;
       cout << "c(n, k) = " << comm(n, k) << endl;
       return 0;
}

例题 2: 有三根针A、B、C。A针上有N个盘子,大的在下,小的在上,要求把这N个盘子从A针移到C针,
在移动过程中可以借助B针,每次只允许移动一个盘,且在移动过程中在三根针上都保持大盘在下,小盘在上。
分析:将n个盘子从A针移到C针可以分解为三个步骤:
1. 将A上n-1个盘子移到B针上(借助C针);
2. 把A针上剩下的一个盘子移到C针上;
3. 将n-1个盘子从B针移到C针上(借助A针)。

源代码:

#include <iostream>
using namespace std;

//将src针的最上面一个盘子移动到dest针上
void move(char src, char dest) { 
    cout << src << " --> " << dest << endl;
}

//将n个盘子从src针移动到dest针,以medium针作为中转
void hanoi(int n, char src, char medium, char dest)
{
  if (n == 1)
    move(src, dest);
  else {
    hanoi(n - 1, src, dest, medium);
    move(src, dest);
    hanoi(n - 1, medium, src, dest);
  }
}
int main() {
  int m;
  cout << "Enter the number of diskes: ";
  cin >> m;
  cout << "the steps to moving " << m << " diskes:" << endl;
  hanoi(m,'A','B','C');
  return 0;
}

运行结果:

Enter the number of diskes:3
the steps to moving 3 diskes:
A --> C
A --> B
C --> B
A --> C
B --> A
B --> C
A --> C

函数的参数传递

  1. 在函数被调用时才分配形参的存储单元

  2. 实参可以是常量、变量或表达式

  3. 实参类型必须与形参相符

  4. 值传递是传递参数值,即单向传递

  5. 引用传递可以实现双向传递

  6. 常引用作参数可以保障实参数据的安全

引用类型

  1. 引用(&)是标识符的别名;

  2. 定义一个引用时,必须同时对它进行初始化,使它指向一个已存在的对象;

  3. 一旦一个引用被初始化后,就不能改为指向其它对象;

  4. 引用可以作为形参。

含可变参数的函数

两种主要的方法:

  1. 如果所有的实参类型相同,则可以传递一个名为initializer_list的标准库类型;
  2. 如果实参的类型不同,则可以编写卡边参数的模板;

initializer_list是一种标准库类型,用于表示某种特定类型的值的数组,该类型定义在同名的头文件中。
initializer_list是一个类模板
使用模板时,我们需要在模板名字后面跟一对尖括号,括号内给出类型参数。例如:

initializer_list<string> ls; // initializer_list的元素类型是string

initializer_list<int> li; // initializer_list的元素类型是int

initializer_list比较特殊的一点是,其对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。
含有initializer_list形参的函数也可以同时拥有其他形参

initializer_list使用举例:
在编写代码输出程序产生的错误信息时,最好统一用一个函数实现该功能,使得对所有错误的处理能够整齐划一。
然而错误信息的种类不同,调用错误信息输出函数时传递的参数也会各不相同。
使用initializer_list编写一个错误信息输出函数,使其可以作用于可变数量的形参。

内联函数

  1. 申明时使用关键字:inline
  2. 编译时在调用处用函数体进行替换,节省了参数传递、控制转移等开销;
  3. 注意:内联函数体内不能由循环语句和switch语句; 内联函数的定义必须出现内联函数第一次被调用之前;对内联函数不能进行异常接口申明。

内联函数应用举例:

#include <iostream>

using namespace std;

const double PI = 3.14159265358979;

inline double calArea(double radius) {

          return PI * radius * radius;

}

int main() {

          double r = 3.0;
          double area = calArea(r);
          cout << area << endl;
          return 0;
}

constexpr函数

语法规定:1. constexpr修饰的函数在其所有参数都是constexpr时,一定返回constexpr;2. 函数体中必须有且仅有一条return语句。
举例: constexpr int get_size() { return 20; }
constexpr int foo = get_size(); //正确:foo是一个常量表达式

带默认参数的函数

默认参数值:可以预先设置默认的参数值,调用时如给出实参,则采用实参值,否则采用预先设置的默认参数值。
例:

int add(int x = 5,int y = 6) {
     return x + y;
}

int main() {
     add(10,20);  //10+20
     add(10);     //10+6
     add();       //5+6
}

默认参数值的说明次序:

  1. 有默认参数的形参必须列在形参列表的最右,即默认参数值的右面不能有无默认值的参数;
  2. 调用时实参与形参的结合次序是从左向右。
    例:
    int add(int x, int y = 5, int z = 6);//正确

int add(int x = 1, int y = 5, int z);//错误

int add(int x = 1, int y, int z = 6);//错误

默认参数值与函数的调用位置:
如果一个函数有原型声明,且原型声明在定义之前,则默认参数值应在函数原型声明中给出;
如果只有函数的定义,或函数定义在前,则默认参数值可以函数定义中给出。
例:

int add(int x = 5, int y = 6);  int add(int x = 5, int y = 6) {
//原型申明在前                          //只有定义,没有原型申明
int main() {                            return x +y;
     add();               }
}                    int main() {
int add(int x, int y) {                add();
//此处不能在指定默认值              }
      return x +y;
}

函数的重载

C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。
重载的函数的函数名是相同的,但它们的参数的个数和数据类型不同,编译器根据实参和形参的类型及个数的最佳匹配,自动确定调用哪一个函数。
例:
int add(int x, int y);
float add(float x, float y); //形参类型不同

int add(int x, int y);
int add(int x, int y, int z); //形参个数不同

注意事项:

  1. 重载函数的形参必须不同:个数不同或类型不同。
  2. 编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用哪一个函数。
    (编译器不以形参名、返回值来区分)
  3. 不要将不同功能的函数申明为重载函数,以免出现调用结果的误解、混淆。

应用举例:
编写两个名为sumOfSquare的重载函数,分别求两整数的平方和及两实数的平方和。

源代码:

#include <iostream>
using namespace std;
int sumOfSquare(int a, int b) {
    return a * a + b * b;
}
double sumOfSquare(double a, double b) {
    return a * a + b * b;
}
int main() {
    int m, n;
    cout << "Enter two integer: ";
    cin >> m >> n;
    cout<<"Their sum of square: "<<sumOfSquare(m, n)<<endl;
    double x, y;
    cout << "Enter two real number: ";

运行结果:

Enter two integer: 3 5
Their sum of square: 34
Enter two real number: 2.3 5.8
Their sum of square: 38.93

tip:
rand函数

  1. 函数原型:int rand(void)

  2. 所需头文件:<cstdlib>;

  3. 功能和返回值:求出并返回一个伪随机数

srand函数

  1. void srand(unsigned int seed);

  2. 参数:``seed`产生随机数的种子

  3. 所需头文件:<cstdlib>

  4. 功能:为使rand()产生一序列伪随机整数而设置起始点。使用1作为seed参数,可以重新初化rand()。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Balaaam

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值