Functions: C++'s Programming Modules

在这一章中要学习以下内容:

  • 函数基础
  • 函数原型
  • 通过value向函数传递参数
  • 设计处理数组的函数
  • 使用const指针参数
  • 设计函数处理文本字符串
  • 设计函数处理结构体
  • 设计函数处理string类型的对象
  • 函数的递归
  • 指向函数的指针(函数指针)

C++有一个庞大的有用的函数库(standard ANSI C library + 几个C++类),但是解决实际问题还需要定义我们自己的函数。

当然为了提高效率也需要STL和BOOST这些C++库。

函数回顾

为了使用C++函数,你需要做以下的事情:

  • 提供一个函数定义(function definition)
  • 提供一个函数原型(function prototype)
  • 调用函数(call the function)

下面提供了一个简单的例子

#include <iostream>

// function prototype
void simple();

int main() {
    using namespace std;
    cout << "main() will call the simple() function:\n";
    // function call
    simple();
    cout << "main() is finished with the simple() function.\n";
    return 0;
}

// function definition
void simple() {
    using namespace std;
    cout << "I'm but a simple function.\n";
}

 

在main函数和simple函数中都放了一个using指令,是因为每个函数都使用了cout

定义一个函数

函数可以分为两类:

  • 没有返回值的
  • 有返回值的

没有返回值的相下面这样

void functionName(parameterList) {
    statement(s)
    // return是可选的
    return;
}

 

parameterList指定了传递给函数的参数的类型和个数。

可选的返回语句标志着函数的结束。

函数的返回值不能是数组,但可以是数组的指针。

函数是如何返回值的?

一般来说,函数是通过复制返回值到指定的CPU寄存器或者内存位置来返回函数值的。

然后调用者会检查那个位置。

调用函数和被调用函数必须对那个位置上返回类型达成一致。

原型和函数调用

函数的原型一般都放在include文件中。

#include <iostream>

// 无返回值的函数原型
void cheers(int);
// 有返回值的函数原型
double cube(double x);

int main() {
    using namespace std;
    // 函数调用
    cheers(5);
    cout << "Give me a number: ";
    double side;
    cin >> side;
    // 函数调用
    double volume = cube(side);
    cout << "A " << side << "-foot cube has a volume of ";
    cout << volume << " cubic feet.\n";
    cheers(cube(2));
    return 0;
}

void cheers(int n) {
    using namespace std;
    for (int i = 0; i < n; i++)
        cout << "Cheers! ";
    cout << endl;
}

double cube(double x) {
    return x * x * x;
}

 

为什么C++需要函数原型呢?

函数原型向编译器描述了函数接口。

第一,原型告诉了编译器函数应该接受几个以及什么类型的参数。

第二,编译器需要知道要几个字节以及何种类型来解释返回值。没有这些信息,编译器只能靠猜。

为什么C++编译器不去文件后面找函数定义呢?

原型符号(Prototype Syntax)

函数原型是一条语句,所以必须以分号结尾。

函数原型只需要类型信息就够了,参数名是什么、要不要都无所谓,参数名只是起到占位符的作用。

函数原型为你做了什么

函数原型极大地减少了程序出错的几率。

函数原型可以保证:

  • 编译器可以正确地处理返回值
  • 编译器可以检查函数调用的参数是否正确
  • 编译器检查是否使用了正确类型的参数

函数原型的检查发生在编译器,被称为静态类型检查(static type check)

通过值传递函数参数

C++一般通过值来传递参数(by value)。

数值类型的值会传递给一个新的变量。

通过值传递的参数称为formal argument或者formal parameter。

而传递给函数的值称为actual argument或者actual parameter。

为了简化一点,C++用argument指实际参数,parameter称为形式参数。

在函数中声明的变量,包括parameters,都是函数私有的。

当函数调用的时候,计算机便为这些变量分配内存;当函数结束时,会释放这些变量。

多参数

传递给函数的多个参数用逗号分隔。

#include <iostream>

long double probability(unsigned numbers, unsigned picks);
int main() {
    using namespace std;
    double total, choices;
    cout << "Enter the total number of choices on the game card and\n"
        "the number of picks allowed:\n";
    while((cin >> total >> choices) && choices <= total) {
        cout << "You have one chance in ";
        cout << probability(total, choices);
        cout << " of winning.\n";
        cout << "Next two numbers (q to quit): ";
    }
    cout << "bye\n";
    return 0;
}

long double probability(unsigned numbers, unsigned picks) {
    long double result = 1.0;
    long double n;
    unsigned p;
    for(n = numbers, p = picks; p > 0; n--, p--)
        result = result * n / p;
    return result;
}

这里要注意的cin >> total为啥输入q就退出了呢?

更多数组函数的例子

 

使用数组范围的函数

一般处理数组的函数都需要传入起始地址和数组的size,但是另一种方式是传入起始和结束指针就可以了。

指针和const

int sloth = 3;
// 不能修改指针指向的对象的值, 但是可以改变指向
const int * ps = &sloth;
// 不能改变指针的指向, 但是可以修改指向的对象的值
int * const finger = &sloth;
// 既不能改变指向元素的值也不能改变元素的指向
const int * const me = &sloth;

 

下面这种双const还是可以用的呀

#include <iostream>
void test(const int * const p);
int main() {
    int a = 10;
    test(&a);

}

void test(const int * const p) {
    std::cout << "p = " << *p << std::endl;
}

函数的指针

如果不提函数指针的话,讲C/C++中的函数是不完整的。

函数和数据一样也是有地址的。地址真的是个神奇的东西。函数的地址就是存储的机器代码在内存中开始的地方。

函数地址对你和用户来说都不重要,但是对于程序来说很重要。

可以用指向函数的指针去调用这个函数,注意指针已经是变量了,变量是可以搞事情的。

为了使用函数的指针,你需要做以下事情。

  • 获取一个函数的地址
  • 声明一个指针指向这个地址
  • 使用这个指针去调用这个函数

获取函数的地址

获取函数的值非常简单,只要函数名,不要函数后面的括号就行。

比如think()是一个函数,think就是函数的地址。

为了将函数作为变量传递到另一个函数中,只需要传递函数名就可以了。

要注意区分的是你是传递的函数名还是传递的函数的返回值。

// 传递think函数的指针
process(think);
// 传递的think函数的返回值
thought(think());

 

声明一个指向函数的指针

声明的函数指针类型应该和函数的返回值以及函数的签名一致。

// 函数原型
double pam(int);
// 对应的函数指针
double (*pf) (int);

 

其实这个步骤挺简单的,把函数原型拿过来,然后把函数名改称(*pf)就可以了。

但是要注意如下这种情况。

// 指向返回值为double类型的函数的指针
double (*pf) (int);
// 返回类型为double *类型的名为pf的函数
double *pf (int);

 

看来这里面的坑还挺多的。

double ned(double);
int ted(int);
double (*pf) (int);
// 参数类型不匹配
pf = ned;
// 返回值不匹配不匹配
pf = ted;

// 声明以函数指针为参数的函数
void estimate(int lines, double (*pf) (int));
// 调用以函数指针作为参数的函数
estimate(50, pam);

 

使用指针去调用函数

double pam(int);
double (*pf) (int);
// 让pf指向函数pam
pf = pam;
// 通过函数名调用
double x = pam(4);
// 通过函数指针调用
double y = (*pf) (5);

 

C++中也可以这么调用,把函数指针当作函数名。

double y = pf(5);

 

函数指针例子

#include <iostream>

double betsy(int);
double pam(int);
void estimate(int lines, double (*pf) (int));

int main() {
    using namespace std;
    int code;

    cout << "How many lines of code do you need? ";
    cin >> code;
    cout << "Here's Betsy's estimate:\n";
    estimate(code, betsy);
    cout << "Here's Pam's estimate:\n";
    estimate(code, pam);
    return 0;

}

double betsy(int lns) {
    return 0.05 * lns;
}

double pam(int lns) {
    return 0.03 * lns + 0.0004 * lns * lns;
}

void estimate(int lines, double (*pf) (int)) {
    using namespace std;
    cout << lines << " lines will take ";
    cout << (*pf)(lines) << " hour(s)\n";
}

 

函数的指针是个神器呀!

函数指针其他主题

主题个屁,就是函数指针的坑。

其他主题太坑了,很容易阵亡,还是留到下一篇学吧。

转载于:https://www.cnblogs.com/tuhooo/p/9932462.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值