C++学习——第8章 使用函数编程
来源:互联网 发布:香港4g网络频段2017 编辑:程序博客网 时间:2024/05/10 16:41
1. 函数
1.1 定义函数
double power (double x, int n)
其中包含三部分:返回值的数据类型(double),函数名power和在圆括号中的函数的参数列表。注意:在函数头的最后不需要加上分号。
一般形式:
返回类型 函数名(参数列表)
如果函数没有返回值,返回类型就由void关键字指定。
函数也可以没有任何参数:
void MyFunction()
或者:
void MyFunction(void)
注意参数的类型不能是void
注意:由于用void指定返回类型的函数没有返回值,因此这种函数不能再调用程序的表达式中使用。此函数不等于任何值,测试其值或者给它赋值是没有意义的。以这种方式使用这类函数,编译器会产生错误消息。
1.2 函数体
代码:使用函数
#include <iostream>#include <iomanip>using namespace std;double power(double x, int n){double result = 1.0;if (n >= 0)for (int i = 0; i < n; i++)result *= x;elsefor (int i = 0; i < -n; i++)result /= x;return result;}int main(){cout << endl;for (int i = -3; i <= 3; i++)cout << setw(10) << power(8.0, i);cout << endl;return 0;}
power()函数调用了7次,每次调用时,第一个参数都是8.0,而第二个参数i的值,从-3到+3。输出为7个值,分别是8-3,、8-2、8-1、80、81、82和83。
1.3 参数和变元
在调用函数值,要通过指定的变元把信息传送给函数。在调用时,变元放在函数名后面的圆括号中。如:
cout << std::setw(10) << power(8.0, i);
在调用函数式,指定的变元将代替在函数定义中使用的参数。函数中的代码在执行时,就使用变元值来初始化对应的参数。
1.4 返回值
一般情况下,在调用返回类型不是void的函数时,该函数必须返回一个值,其类型已在函数头中指定。因此在调用函数power()的表达式中,power()实际上是一个double类型的值。
return语句
一般形式:
return expression;
其中,expreesion必须等于在函数头中为返回值指定的类型的值。该表达式还可以是任何表达式,只要其值是指定的类型即可。它可以包含函数调用,甚至可以包含同一函数的调用。
如果返回类型指定为void,return语句中不应有表达式:
return;
2. 函数的声明
可以再使用之前声明函数,或者通过函数原型来定义函数。
函数原型
可以把power()函数的函数原型写为:
double power(double x, int n);
注意有分号
3. 给函数传送参数
3.1 按值传送机制
代码:给函数传送变元——编写一个函数,它试图修改它的一个变元,但是不会成功
#include <iostream>#include <iomanip>using namespace std;double change_it(double it);int main(){double it = 5.0;double result = change_it(it);cout << "After function execution, it = " << it << endl << "Result returned is " << result << endl;return 0;}double change_it(double it){it += 10.0;cout << endl << "Within function, it = " << it << endl;return it;}
从输出中,可以看出,在函数change_it()中给变量it加上10,对main()中的变量it没有任何影响。
函数change_it()中的变量it时该函数的局部变量,在调用该函数时,会引用所传送的变元值的副本。
当然,在返回change_it()的局部变量it的值时,会制作其当前值的副本,把该副本返回给调用程序。
给函数传送指针
代码:传送指针——如上修改变元的值,这次成功了
#include <iostream>#include <iomanip>using namespace std;double change_it(double* pointer_to_it);int main(){double it = 5.0;double result = change_it(&it);cout << "After function execution, it = " << it << endl << "Result returned is " << result << endl;return 0;}double change_it(double* pit){*pit += 10.0;cout << endl << "Within function, it = " << *pit << endl;return *pit;}
double change_it(double* pointer_to_it);
其中在函数原型中的参数名可以与函数定义中的参数名不相同在main()中,声明并初始化变量it后,就再调用change_it()函数时,给它传送变量it的地址:
double result = change_it(&it);
不需要创建一个指针变量来存储it的地址。只需要给函数传送的地址,可以再函数调用使用地址运算符。
给函数传送数组
代码:把数组作为函数的参数传送
#include <iostream>#include <iomanip>using namespace std;double average(double array[], int count);int main(){double values[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};cout << endl << " Average = " << average(values, (sizeof values)/(sizeof values[0])) << endl;return 0;}double average(double array[], int count){double sum = 0.0;for (int i = 0; i < count; i++)sum += array[i];return sum/count;}
double average(double array[], int count);
从函数原型中可以看出,它接受两个参数,数组和数组中元素的个数:第一个参数指定为double类型的数组,不能在方括号中指定数组的大小,即使指定也没有效果。这是因为数组第一维的大小不是其类型的一部分(在考虑多维数组的指针时,也会有类似的问题)。
代码:在传送数组时使用指针表示法
#include <iostream>#include <iomanip>using namespace std;double average(double* array, int count);int main(){double values[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};cout << endl << " Average = " << average(values, (sizeof values)/(sizeof values[0])) << endl;return 0;}double average(double* array, int count){double sum = 0.0;for (int i = 0; i < count; i++)sum += *array++;return sum/count;}
这里的for循环,很有意思:注意修改的地方,函数原型和函数定义的参数,不是带有方括号的数组名,而是*array。
sum += *array++;
按值传送机制复制了原数组地址,并把副本传送给函数。我们修改的是副本,原数组地址并没有修改。一般情况下,只要给函数传送一维数组,就可以把所传送的值作为指针,以任何方式修改地址。(这一段不太懂)
代码:传送多维数组
#include <iostream>#include <iomanip>using namespace std;double yield(double array[][4], int n);int main(){double beans[3][4] = {{1.0, 2.0, 3.0, 4.0}, {5.0, 6.0, 7.0, 8.0},{9.0, 10.0, 11.0, 12.0}};cout << endl << "Yield = " << yield(beans, sizeof beans/sizeof beans[0]) << endl;return 0;}double yield(double array[][4], int count ){double sum = 0.0;for (int i=0; i<count; i++)for (int j=0; j<4; j++)sum += array[i][j];return sum;}
函数yield()的第一个参数定义为一个数组,它有任意多行,但是每行有4个元素。
3.2 按引用传送机制
要指定引用类型,只需在类型名的后面加上&。
引用只是另一个变量的别名,引用类型的变量存储对其他变量的引用。在把函数参数指定为引用类型时,函数就要使用按引用传送机制来传送参数。在调用函数时,对应于引用参数的变元不会复制,因为参数名就是调用程序中该变元值的别名。只要在函数体中使用参数名,它就会直接访问调用函数值中的变元值。
引用有风险:引用很高效,但是也有负面影响(安全性等)。
代码:引用参数
#include <iostream>#include <iomanip>using namespace std;int larger(int& m, int& n);int main(){int value1=10;int value2=20;cout<<endl<<larger(value1, value2)<<endl;return 0;}int larger(int& m, int& n){return m>n ? m : n;}
使用常量引用
int larger(const int& m, const int& n);
现在函数可以处理变量和常量了。只要不打算修改传送过来的参数,就总是可以把参数定义为const。
使用const引用参数,就可以获得引用参数的高性能和高效率,以及按值传送方法的安全性。
引用和指针
在大多数情况下,使用引用参数比使用指针更好。只要可能就应该把引用参数声明为const,因为这会为调用程序提供参数的安全性。
二者区别:指针和引用的一个重要区别是,指针可以为空,而引用总是要引用某个数据项——当然,只要它不是空指针的一个别名。如果允许参数为空,惟一的选择就是指针参数。指针参数可以为空,所以必须在解除对指针的引用之前测试它。如果试图解除对空指针的引用,程序就会崩溃。
声明引用
引用不会显示为函数的参数。引用可以以另一个变量的别名独立存在。假定有一个变量声明为:
long number = 0;
long& rnumber = number;
类型long后面的宏&表示声明一个引用,等号后面的初始化值指定为变量名number。因此,rnumber是“引用long”类型。
同声明指针一样,&也可以放到变量名前面:
long &rnumber = number;
一些约束:
1. 声明引用时,引用必须总是初始化为变量的一个别名。
2. 在声明引用时,决不能不对它进行初始化。(必须初始化)
3. 引用时固定的,在声明之后就不能修改,它总是同一个变量的别名。
使用指针和使用引用有一个显著区别:指针需要解除引用,才能获得或操作它指向的变量值,而引用不需要这一步。在某些方面,引用类似于已解除引用的指针,但引用不能修改为引用另一个变量。
引用是与引用的变量完全等价的。
3.3 main()的参数
函数main()可以有参数:
int main(int argc, char* argv[]){
//Code for main()
}
4. 默认的参数值
在许多情况下,给一个或多个函数参数赋予默认值是非常有用的。这意味着,仅需要在希望参数值不同于默认值时,为参数指定值。
例如:有一个函数要用于输出标准的错误消息。大多数情况下,使用默认的消息就足够了,但有时候需要指定另一个消息。为此,可在函数原型中为参数指定默认值。输出消息的函数的定义如下:
void show_error(const char* message)
{
cout<<endl<<message<<endl;
}
要指定默认消息,可以再这个函数的原型中编写一个字符串,用作默认的参数值,如下:
void show_error(const char* message=”Program Error”);
如果要使用该函数输出默认的消息,在调用它可以不指定参数,如下:
show_error();
该函数会显示结果:
Program Error
如果要提供某个消息,就可以指定函数的参数:
show_error(“Nothing works!”);
下面使用string类型的参数定义show_error()函数,而不是C样式的字符串。
void show_error(const string message=”Program Error”);
代码:使用多个默认参数值
#include <iostream>#include <iomanip>#include <string>using namespace std;void show_data(const int data[], int count=1, const string& title = "Data Values", int width = 10, int perLine = 5);int main(){int samples[]={1,2,3,4,5,6,7,8,9,10,11,12};int dataItem=99;show_data(&dataItem);dataItem=13;show_data(&dataItem, 1, "Unlucky for some!");show_data(samples, sizeof samples/sizeof samples[0]);show_data(samples, sizeof samples/sizeof samples[0], "samples");show_data(samples, sizeof samples/sizeof samples[0], "samples", 14);show_data(samples, sizeof samples/sizeof samples[0], "samples", 14, 4);return 0;}void show_data(const int data[], int count, const string& title, int width, int perLine){cout<<endl<<title;for(int i=0; i<count; i++){if(i % perLine==0)cout<<endl;cout<<setw(width)<<data[i];}cout<<endl;}
5. 从函数中返回值
5.1 返回一个指针
在从函数中返回一个指针时,必须确保它指向的地址是0,或者在调用函数仍旧有效的内存地址。换言之,在指针返回到调用函数中后,指针所指向的变量必须仍在其作用域中。其中的黄金规则:不要从函数中返回自动局部变量的地址。
例如:
int* larger(int* a, int* b)
{
if(*a>*b)
return a;
else
return b;
}
可以用以下代码调试该函数:
*larger(&value1, &value2)=100;
5.2 返回一个引用
引用的黄金规则:不要从函数中返回自动局部变量的引用。
例如:
int& larger(int& m, int& n)
{
return m>n?m:n;
}
返回类型是对int的引用,参数是非常量的引用。我们要返回某个引用参数,就不能把参数声明为const。
用以下语句来使用该函数修改两个参数中的较大值:
larger(value1,value2)=50;
5.3 从函数中返回新变量
在函数中,可以再自由存储区中创建新变量,并通过指针返回值将它返回给调用程序。使用new运算符就可以为新变量分配内存空间,并返回其地址。
危险大,内存泄露的可能性非常高。每次调用这样的函数时,都要在自由存储区中分配更多的内存。调用函数应负责使用delete运算符来释放这些内存。
6. 内联函数
如果函数非常短,编译器为处理传送过来的参数以及返回结果的代码的系统开销,与进行实际计算的代码相比就非常多。这两类代码的执行时间非常相关。
在极端情况下,调用函数的代码占用的内存会比函数体中的代码还多。在这种情况下,编译器就应使用函数体中的实际代码替代函数的调用,并做适当的调整,已处理局部名称。这会使得程序更短更快。
为了让编译器完成此任务,可以在函数的定义中使用inline关键字。如:
inline int larger(int m, int n)
{
return m>n?m:n;
}
通过这个定义,编译器就会用内联代码替代调用。
但是这只是一个建议,它取决于编译器是否采纳这个建议。把函数声明为inline,函数的定义就必须可以再调用函数的每个源文件中使用。因此,内联函数的定义通常放在头文件中,而不是源文件中,该头文件包含在使用该函数的每个源文件中。
(不同的编译器采用不同的规则来确定定义为inline的函数是否用内联代码代替其调用。
有时编译器选择不按照请求把函数看做内联函数,这有个缺点:在这个情况下,函数调用会按照正常的函数调用来编译,但编译器一般还是会把该函数看做源文件的本地函数,所以每个使用它的源文件都要拥有该函数的已编译副本。结果是,如果函数在几个不同的源文件中使用,函数的代码就要进行不必要的重复。
7. 静态变量
前面编写的所有函数中,函数体在每次执行之后都不会保留任何信息。假定要计算某个函数的调用次数,怎么办?
一种方法:在文件作用域定义一个变量,然后再在函数中递增它。但是这个方法不好控制。
另一种方法:在函数体重把该变量声明为static,静态变量。
静态变量在定义它的语句中创建,在此之后,它一直存在,知道程序结束为止。
下面的函数示例声明了一个静态变量:
void nexInterger()
{
static int count=1;
cout<<endl<<count++;
}
只要程序在执行,以后对函数的调用都会使用count的当前值。
代码:在函数中使用静态变量
#include <iostream>#include <iomanip>#include <string>using namespace std;long next_Fibonacci();int main(){cout<<endl<<"The Fibonacci Series"<<endl;for(int i=0; i<30; i++){if(i%5==0)cout<<endl;cout<<setw(12)<<next_Fibonacci();}cout<<endl;return 0;}long next_Fibonacci(){static long last=0;static long last_but_one=1;long next = last + last_but_one;last_but_one = last;last = next;return last;}
只要程序存在,静态变量就存在,但静态变量只能在声明它们的块中访问,所以变量last和last_but_one只能在next_Fibonacci()函数体中访问。
- C++学习——第8章 使用函数编程
- 一站式学习C编程第5章
- C语言基础学习——第3天(函数)
- 《Objective-C编程之道:iOS设计模式解析》学习笔记——第1章
- 【C++】学习笔记十四——C++ primer plus第六版第4章编程练习
- 【C++】学习笔记二十——第5章编程练习
- 高质量C++/C编程指南(第8章 C++函数的高级特性)
- 高质量C++/C编程指南 -- 第8章 C++函数的高级特性
- 高质量C++/C编程指南 -- 第8章 C++函数的高级特性
- 高质量C++/C编程指南 -- 第8章 C++函数的高级特性
- 高质量C++/C编程指南 -- 第8章 C++函数的高级特性
- 高质量C++/C编程指南 -- 第8章 C++函数的高级特性
- 高质量C++/C编程指南 -- 第8章 C++函数的高级特性
- C primer ++ 学习笔记第8篇——C++函数
- C语言基础学习——第8天(函数指针)
- C Primer Plus 第9章 函数 编程练习
- 犀牛——第8章函数 8。8returnret8.8函数式编程
- Linux C编程函数学习
- Deb Package to RPM package
- 数独2012 07 11
- install mango db on ubuntu
- 欧拉计划十一题:在20×20的网格中同一直线上四个数的最大乘积是多少?(perl二维数组的应用)
- android 游戏开发连接
- C++学习——第8章 使用函数编程
- 【OpenGL】理解GL_TRIANGLE_STRIP等绘制三角形序列的三种方式
- 消费者&生产者模型的python代码
- DCD 伪指令解析
- 招聘软件测试实习生
- 成都一周之忙里偷闲
- UESTC 1784 Krolia的计时难题
- 集合三
- gvim与插件的安装(ctag、taglist、cscope等)