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";
    ;
}
// 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();
    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());
    ;
}
void cheers(int n) {
    using namespace std;
    ; 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";
    ;
}
long double probability(unsigned numbers, unsigned picks) {
    long double result = 1.0;
    long double n;
    unsigned p;
    ; n--, p--)
        result = result * n / p;
    return result;
}
这里要注意的cin >> total为啥输入q就退出了呢?
更多数组函数的例子
使用数组范围的函数
一般处理数组的函数都需要传入起始地址和数组的size,但是另一种方式是传入起始和结束指针就可以了。
指针和const
; // 不能修改指针指向的对象的值, 但是可以改变指向 const int * ps = &sloth; // 不能改变指针的指向, 但是可以修改指向的对象的值 int * const finger = &sloth; // 既不能改变指向元素的值也不能改变元素的指向 const int * const me = &sloth;
下面这种双const还是可以用的呀
#include <iostream>
void test(const int * const p);
int main() {
    ;
    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(, pam);
使用指针去调用函数
double pam(int); double (*pf) (int); // 让pf指向函数pam pf = pam; // 通过函数名调用 ); // 通过函数指针调用 );
C++中也可以这么调用,把函数指针当作函数名。
);
函数指针例子
#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);
    ;
}
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";
}
函数的指针是个神器呀!
函数指针其他主题
主题个屁,就是函数指针的坑。
其他主题太坑了,很容易阵亡,还是留到下一篇学吧。
Functions: C++'s Programming Modules的更多相关文章
- The Python Standard Library
		The Python Standard Library¶ While The Python Language Reference describes the exact syntax and sema ... 
- C语言学习书籍推荐《Practical C++ Programming》下载
		下载链接 :点我 C++ is a powerful, highly flexible, and adaptable programming language that allows software ... 
- Linux LSM(Linux Security Modules) Hook Technology
		目录 . 引言 . Linux Security Module Framework Introduction . LSM Sourcecode Analysis . LSMs Hook Engine: ... 
- Functional programming idiom
		A functional programming function is like a mathematical function, which produces an output that typ ... 
- ansible modules开发(一)
		一 模块说明 官方是否有提供的类似功能模块? 可从下面两个连接确定官方提供的模块,以免重复造轮子 官方已发布的模块 http://docs.ansible.com/ansible/modules.ht ... 
- Returning Values from Bash Functions
		转自:https://www.linuxjournal.com/content/return-values-bash-functions Bash functions, unlike function ... 
- [SCSS] Write Custom Functions with the SCSS @function Directive
		Writing SCSS @functions is similar to writing functions in other programming languages; they can acc ... 
- Writing device drivers in Linux: A brief tutorial
		“Do you pine for the nice days of Minix-1.1, when men were men and wrote their own device drivers?” ... 
- Introspection in Python  How to spy on your Python objects  Guide to Python introspection
		Guide to Python introspection https://www.ibm.com/developerworks/library/l-pyint/ Guide to Python in ... 
随机推荐
- EasyUI 中GridView 满足某条件 改变行的背景色
			<table id='grid' class='easyui-datagrid' style='width:1500px;height:450px' url='Ajax-index.php?mo ... 
- openkm预览功能报错:flexpaper License key not accepted(no key passed to viewer)
			openkm:6.3.4 使用google浏览器打开,想预览文件,但是pdf.word和图片都不能显示.只是显示空白. 换成IE后,再次尝试,发现了报错信息: 解决方案: 1- Stop openkm ... 
- java 和 C++ Socket通信(java作为服务端server,C++作为客户端client,解决中文乱码问题GBK和UTF8)
			原文链接: http://www.cnblogs.com/kenkofox/archive/2010/04/25/1719649.html 代码: http://files.cnblogs.com/k ... 
- SharePoint 2013 启用 查看PDF功能
			SharePoint 2013 默认不能直接Online (注:此Online非OWA概念,而是可以实现直接调用客户端软件实现对文档的编辑,保存之后同步上传)打开PDF(SharePoint 2013 ... 
- oracle数据库rman异地恢复
			自己想做两组rac之间的data guard,由于datafile,controlfile,甚至是archivelog都是存放在asm上的,直接复制数据有点不现实,asm磁盘总归都是要用的,所以想从a ... 
- [转]gdb调试多进程和多线程命令
			1. 默认设置下,在调试多进程程序时GDB只会调试主进程.但是GDB(>V7.0)支持多进程的分别以及同时调试,换句话说,GDB可以同时调试多个程序.只需要设置follow-fork-mode( ... 
- 转: FFmpeg功能命令汇总
			原文: FFmpeg功能命令汇总 前言 如此强大的FFmpeg,能够实现视频采集.视频格式转化.视频截图.视频添加水印.视频切片.视频录制.视频推流.更改音视频参数功能等.通过终端命令如何实现这些功能 ... 
- C#两个DataTable拷贝问题:该行已经属于另一个表的解决方法
			一.DataTable.Rows.Add(DataRow.ItemArray); 二.DataTable.ImportRow(DataRow) 三.设置DataTable的tablename,然后.R ... 
- 开发前奏曲之添加Android SDK平台工具
			原文:http://android.eoe.cn/topic/android_sdk Android SDK分离不同部位的SDK成单独的下载包.您已经安装只包含SDK工具的SDK入门包.要开发一个An ... 
- fpm制做mysql-5.6.33 rpm包
			增加用户: # groupadd -r mysql # useradd -g mysql -r -s /sbin/nologin -M -d /data/my_db mysql 源码安装mysql-5 ... 
